<template>
  <div class="data-viewer" tabindex="1" ref="dataViewerContainer" @keydown="handleKeyDown" @click="setFocus">
    <!-- 添加图表区域的loading遮罩层 -->
    <div v-if="isLoading" class="loading-overlay">
      <div class="spinner"></div>
    </div>
    
    <div class="controls">
      <div class="flex items-center gap-2">
        <!-- <button v-if="mode === 'online'" :class="['tech-btn', isPreview ? 'tech-btn-active' : '']"  @click="showPreview">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
          </svg>
          全景预览
        </button> -->
        <!-- 移除游标模式按钮 -->
        <button :class="['tech-btn', interactionMode === 'measure' ? 'tech-btn-active' : '']" @click="handleMeasure">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M2 12h20"></path>
            <path d="M6 8v8"></path>
            <path d="M18 8v8"></path>
            <path d="M2 16h20"></path>
          </svg>
          测量
        </button>

        <!-- 移除缩放模式按钮，直接显示缩放类型选择 -->
        <button
          :class="['tech-btn', zoomType === 'horizontal' ? 'tech-btn-active' : '']"
          @click="setZoomType('horizontal')">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="5" y1="12" x2="19" y2="12"></line>
            <polyline points="12 5 19 12 12 19"></polyline>
          </svg>
          水平缩放
        </button>
        <button
          :class="['tech-btn', zoomType === 'vertical' ? 'tech-btn-active' : '']"
          @click="setZoomType('vertical')">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <polyline points="5 12 12 19 19 12"></polyline>
          </svg>
          纵向缩放
        </button>
        <button
          :class="['tech-btn', zoomType === 'rect' ? 'tech-btn-active' : '']"
          @click="setZoomType('rect')">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
            <line x1="8" y1="3" x2="8" y2="21"></line>
            <line x1="16" y1="3" x2="16" y2="21"></line>
            <line x1="3" y1="8" x2="21" y2="8"></line>
            <line x1="3" y1="16" x2="21" y2="16"></line>
          </svg>
          矩形缩放
        </button>
        <button class="tech-btn" @click="resetZoom">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
            <path d="M3 3v5h5"></path>
          </svg>
          重置缩放
        </button>
        <!-- 保留多边形选择按钮 -->
        <!-- <button
          :class="['tech-btn', interactionMode === 'polygon' ? 'tech-btn-active' : '']"
          @click="interactionMode === 'polygon' ? switchToMode(null) : switchToMode('polygon')">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"></polygon>
            <line x1="8" y1="2" x2="8" y2="18"></line>
            <line x1="16" y1="6" x2="16" y2="22"></line>
          </svg>
          多边形选择
        </button> -->
        
        <button v-if="interactionMode === 'polygon'" class="tech-btn" @click="clearPolygon">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M3 6h18"></path>
            <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"></path>
            <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
            <line x1="10" y1="11" x2="10" y2="17"></line>
            <line x1="14" y1="11" x2="14" y2="17"></line>
          </svg>
          清除多边形
        </button>
        <button class="tech-btn" @click="openViewSettings">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="3"></circle>
            <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
          </svg>
          视图设置
        </button>

        <button class="tech-btn" @click="toggleTheme">
          <svg v-if="isDarkMode" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="5"></circle>
            <line x1="12" y1="1" x2="12" y2="3"></line>
            <line x1="12" y1="21" x2="12" y2="23"></line>
            <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
            <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
            <line x1="1" y1="12" x2="3" y2="12"></line>
            <line x1="21" y1="12" x2="23" y2="12"></line>
            <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
            <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
          </svg>
          <svg v-else xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
          </svg>
          {{ isDarkMode ? '明亮色' : '黑夜色' }}
        </button>

        <button  @click="handleFaultManage" :class="['tech-btn', isFaultModeActive ? 'tech-btn-active' : '']">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
            <line x1="12" y1="9" x2="12" y2="13"></line>
            <line x1="12" y1="17" x2="12.01" y2="17"></line>
          </svg>
          故障显示
        </button>

        <!-- 工具下拉菜单 -->
        <!-- <div class="tools-dropdown" ref="toolsDropdown">
          <button class="tech-btn" @click="toggleToolsDropdown">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
            </svg>
            工具
            <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <polyline points="6,9 12,15 18,9"/>
            </svg>
          </button>
          <div v-if="showToolsDropdown" class="dropdown-menu">
            <div class="dropdown-item" @click="handleVirtualParam" v-if="mode != 'tinker'">
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M8 2v3"/>
                <path d="M16 2v3"/>
                <path d="M21 13V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8"/>
                <path d="M3 10h18"/>
                <path d="m16 20 2 2 4-4"/>
              </svg>
              虚拟参数
            </div>
            <div class="dropdown-item" @click="handleExportData">
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <path d="M12 5v14"/>
                <path d="M19 12l-7 7-7-7"/>
                <path d="M5 19h14"/>
              </svg>
              数据导出
            </div>
            <div class="dropdown-item" @click="handleSimilaritySearch" v-if="mode != 'tinker'">
              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                <circle cx="11" cy="11" r="8"/>
                <path d="m21 21-4.35-4.35"/>
                <circle cx="11" cy="8" r="2"/>
                <circle cx="8" cy="14" r="2"/>
                <circle cx="14" cy="14" r="2"/>
              </svg>
              相似性检索
            </div>
          </div>
        </div> -->

        <!-- <button 
          :class="['tech-btn', useSharedYAxis ? 'tech-btn-active' : '']" 
          @click="toggleYAxisMode">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="3" y1="12" x2="21" y2="12"></line>
            <line x1="3" y1="6" x2="21" y2="6"></line>
            <line x1="3" y1="18" x2="21" y2="18"></line>
          </svg>
          {{ useSharedYAxis ? '共享Y轴' : '独立Y轴' }}
        </button> -->
      </div>

      
      <div v-if="interactionMode === 'polygon'" class="keyboard-hint text-sm text-gray-500">
        提示: 点击图表添加多边形顶点，再次点击第一个顶点完成多边形绘制
      </div>
      <div v-else-if="zoomType === 'rect'" class="keyboard-hint text-sm text-gray-500">
        提示: 向右下框选区域进行自由缩放，其他方向框选回退缩放
      </div>
      <div v-else-if="zoomType === 'vertical'" class="keyboard-hint text-sm text-gray-500">
        提示: 向下拖动放大选择区域，向上拖动回退缩放
      </div>
      <div v-else-if="interactionMode === 'measure'" class="keyboard-hint text-sm text-gray-500">
        提示: 点击右侧参数列表中的数值加入测量，双击测量结果中参数名移除测量
      </div>
      <!-- 添加全局键盘提示，不再是基于模式的 -->
      <div v-else class="keyboard-hint text-sm text-gray-500">
        提示: 使用键盘 ← → 方向键移动游标，向右拖动放大选择区域，向左拖动回退缩放
      </div>
    </div>
    <div ref="chartContainer" class="chart-container"></div>

    <div v-if="showPolygonData" class="modal-overlay">
      <!-- <input id="polygon-drawer" type="checkbox" class="drawer-toggle" checked /> -->
      <div class="modal-content modal-main">
        <div class="modal-header">
          <h3>多边形区域坐标点</h3>
          <button class="close-btn" @click="showPolygonData = false">&times;</button>
        </div>
          <div class="polygon-data">
            <div class="polygon-info mb-4">
              <p>多边形顶点数量: {{ polygonPoints.length }}</p>
            </div>
            <div class="overflow-x-auto">
              <table class="table table-xs">
                <thead>
                  <tr>
                    <th v-for="col in polygonColumns" :key="col.key">{{ col.title }}</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="item in polygonTableData" :key="item.key">
                    <td>{{ item.index }}</td>
                    <td>{{ item.dataX }}</td>
                    <td>{{ item.dataY }}</td>
                  </tr>
                </tbody>
              </table>
            </div>
            <div class="polygon-actions mt-4 flex gap-2">
              <button class="tech-btn btn-sm" @click="copyPolygonData">复制数据</button>
              <!-- <button class="btn btn-sm" @click="exportPolygonData">导出CSV</button> -->
            </div>
          </div>
        </div>
    </div>
  </div>
  <!-- 视图设置模态框 -->
  <div v-if="showViewSettingsModal" class="modal-overlay" @click.self="cancelViewSettings">
    <div class="modal-content modal-main">
      <div class="modal-header">
        <h3>视图设置</h3>
        <button class="close-btn" @click="cancelViewSettings">&times;</button>
      </div>
      <div class="modal-body">
        <div class="settings-list">
          <div v-if="selectedParametersListInView.length == 0" class="no-data-message">暂未选择参数</div>
          <div v-for="paramName in selectedParametersListInView || []" :key="paramName" class="param-section">
            
            <!-- 参数级别设置 -->
            <div class="param-setting-row">
              <div class="setting-param-name">{{ getShowName(getParameterName(paramName)) }}</div>

              <!-- 颜色选择器 -->
              <div class="control-group">
                <label>颜色：</label>
                <input
                  type="color"
                  :value="getParameterBaseColor(paramName)"
                  @change="(e: Event) => updateParameterSetting(paramName, { color: (e.target as HTMLInputElement).value })"
                />
              </div>

              <!-- Y轴范围设置 -->
              <div class="control-group">
                <label>Y轴范围：</label>
                <div class="y-axis-range">
                  <label>下限</label>
                  <input
                    type="number"
                    placeholder="下限"
                    class="range-input"
                    :value="getParameterYAxisMin(paramName)"
                    @change="(e: Event) => updateParameterSetting(paramName, { yAxisMin: (e.target as HTMLInputElement).value ? parseFloat((e.target as HTMLInputElement).value) : undefined })"
                  />
                  <label>上限</label>
                  <input
                    type="number"
                    placeholder="上限"
                    class="range-input"
                    :value="getParameterYAxisMax(paramName)"
                    @change="(e: Event) => updateParameterSetting(paramName, { yAxisMax: (e.target as HTMLInputElement).value ? parseFloat((e.target as HTMLInputElement).value) : undefined })"
                  />
                </div>
              </div>

              <!-- 线条粗细选择 -->
              <div class="control-group">
                <label>粗细：</label>
                <input
                  type="number"
                  class="range-input"
                  min="0.5"
                  max="10"
                  step="0.5"
                  :value="getParameterLineWidth(paramName)"
                  @change="(e: Event) => updateParameterSetting(paramName, { lineWidth: parseFloat((e.target as HTMLInputElement).value) })"
                />
              </div>
                <!-- 显示方式选择 -->
              <div class="control-group">
                <label>显示方式：</label>
                <select
                  class="search-select"
                  :value="getParameterDisplayMode(paramName)"
                  @change="(e: Event) => updateParameterSetting(paramName, { displayMode: (e.target as HTMLSelectElement).value as 'continuous' | 'discrete' })"
                >
                  <option value="continuous">连续量</option>
                  <option value="discrete">离散量</option>
                </select>
              </div>

              <!-- 显示标签选择 -->
              <div class="control-group">
                <label class="tech-checkbox">
                  <input
                    type="checkbox"
                    :checked="getParameterShowLabel(paramName)"
                    @change="(e: Event) => updateParameterSetting(paramName, { showLabel: (e.target as HTMLInputElement).checked })"
                  />
                  <span class="checkbox-custom"></span>
                  <span>显示标签</span>
                </label>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button class="tech-btn-warn view-btn-cancel" @click="cancelViewSettings">取消</button>
        <button class="tech-btn view-btn-primary" @click="saveViewSettings" :disabled="selectedParametersList.length == 0">保存</button>
      </div>
    </div>
  </div>
  <!-- 消息提示组件 -->
  <Message ref="messageRef" />
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick, onBeforeUnmount } from 'vue'
import * as d3 from 'd3'
import type { ParameterViewSetting } from '../../types'
import API from './chartData.api.ts'
import Message from '@/components/BaseComponents/Message.vue';
import { useFontSettingsStore } from '@/stores/fontSettings';

const messageRef = ref<InstanceType<typeof Message> | null>(null)
const samplePoints = computed(() => localStorage.getItem("samplePoints") || 500)
const unitCode = computed(() => localStorage.getItem("unitCode"))
const startTimeLocal = computed(() => localStorage.getItem("startTime"))
const endTimeLocal = computed(() => localStorage.getItem("endTime"))
const mode = computed(() => localStorage.getItem('mode'))
const importFileIndex = computed(() => localStorage.getItem('importFileIndex'))
const flag = computed(() => localStorage.getItem('flag'))
const deviceIdentifie = computed(() => localStorage.getItem('rjNo'))
// 添加API选择助手函数
const getApiMethod = () => {
  return API.queryRJData // 默认使用queryData
}
// 主题设置相关变量 默认深色
const isDarkMode = ref(true)
// 使用字体设置 store
const fontSettingsStore = useFontSettingsStore();

// 获取当前线条类型
const currentLineType = computed(() => fontSettingsStore.lineType || 'curve');

// 获取当前时间间隔
const timeIntervalList = ref()

// 根据线条类型获取对应的 D3 曲线类型
const getLineType = () => {
  console.log('currentLineType:', currentLineType.value);
  return currentLineType.value === 'curve' ? d3.curveMonotoneX : d3.curveLinear;
};
// 监听线条类型变化
watch(currentLineType, (newLineType, oldLineType) => {
  if (newLineType !== oldLineType) {
  console.log('DataViewer - Line type changed to:', newLineType);
  // 当线条类型变化时，重新绘制图表
  nextTick(() => {
    if (svg && chartContainer.value) {
      updateChartWithParamYScales();
    }
  });
}
}, { deep: true, immediate: true });
// 添加参数准备助手函数
const prepareApiParams = (startTime?: number | string, endTime?: number | string, fullData: boolean = false) => {
  let columnList = selectedParamsInfo.value
  // 将 columnList 转换为 { id: name, id: name } 的对象格式
  const columnListObj = columnList.reduce((acc, item) => {
    acc[item.id] = item.name;
    return acc;
  }, {} as { [key: string]: string })
  //RJ模式的startTime 和 endTime 需要是 YYYY-MM-DD HH:MM:SS格式
  let xStartPoint = ""
  let xEndPoint = ""
  if (
    startTime == "Invalid Date" ||
    startTime == undefined ||
    startTime == null
  ) {
    xStartPoint = ""
  } else {
    // const year = startTime.getFullYear();
    // const month = String(startTime.getMonth() + 1).padStart(2, '0');
    // const day = String(startTime.getDate()).padStart(2, '0');
    // const hour = String(startTime.getHours()).padStart(2, '0');
    // const minute = String(startTime.getMinutes()).padStart(2, '0');
    // const second = String(startTime.getSeconds()).padStart(2, '0');
    // xStartPoint = `${year}-${month}-${day} ${hour}:${minute}:${second}`
    xStartPoint = startTime
      .toLocaleString("zh-CN", {
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit",
      })
      .replace(/\//g, "-")
  }
  if (endTime == "Invalid Date" || endTime == undefined || endTime == null) {
    xEndPoint = ""
  } else {
    // const year = endTime.getFullYear();
    // const month = String(endTime.getMonth() + 1).padStart(2, '0');
    // const day = String(endTime.getDate()).padStart(2, '0');
    // const hour = String(endTime.getHours()).padStart(2, '0');
    // const minute = String(endTime.getMinutes()).padStart(2, '0');
    // const second = String(endTime.getSeconds()).padStart(2, '0');
    // xEndPoint = `${year}-${month}-${day} ${hour}:${minute}:${second}`
    xEndPoint = endTime
      .toLocaleString("zh-CN", {
        year: "numeric",
        month: "2-digit",
        day: "2-digit",
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit",
      })
      .replace(/\//g, "-")
  }

  return {
    startTime: xStartPoint,
    endTime: xEndPoint,
    sample: samplePoints.value,
    unitCode: unitCode.value,
    params: columnListObj,
  }
}

// 数字格式化函数，用于Y轴刻度显示
const formatAxisNumber = (domainValue: d3.NumberValue, index: number): string => {
  const value = Number(domainValue);
  const absValue = Math.abs(value);
  
  // 如果是0，直接返回
  if (value === 0) return '0';
  
  // 如果数值很大（>=100000）或很小（<0.001且不为0），使用科学计数法
  if (absValue >= 100000 || (absValue < 0.001 && absValue !== 0)) {
    return value.toExponential(2);
  }
  
  // 如果数值在1-99999之间，保留适当的小数位
  if (absValue >= 1) {
    return value.toFixed(2);
  }
  
  // 如果数值在0.001-1之间，保留3位小数
  return value.toFixed(3);
};

const props = defineProps<{
  selectedParameters?: string[]
  selectedVirtualParamsName?: string[]
  selectedVirtualParamsInfo?: string[]
  viewSettings?: Record<string, ParameterViewSetting>
  selectedParametersInfo?: { id: string, name: string, type: string }[]
  selectedChannel?: Array<{channelId: string, fileName: string, parameters: string}>
  isSiderCollapsed?: boolean
  changeTask?: boolean
}>()
const isChangeTask = ref(false)
const selectedParametersList = ref<string[]>([]) // 保存选中的参数列表,包含RecTime
const selectedParametersListInView = ref<string[]>([]) // 视图设置中显示的参数列表
const selectedParamsInfo = ref<string[]>([]) // 选中参数信息

const emit = defineEmits<{
  (e: 'update:cursorPosition', value: number): void
  (e: 'update:measurementData', value: { cursor1: number, cursor2: number, deltaTime: number, measurementResults: Record<string, { parameterId: string, channelId: string, min: number, max: number, avg: number, fluctuation: number }> } | undefined): void
  (e: 'update:cursorData', value: Record<string, Record<string, { value: number, timestamp: number }>>): void
  (e: 'update:viewSettings', value: Record<string, ParameterViewSetting>): void
  (e: 'update:selectedParametersList', value: string[]): void
  (e: 'update:darkMode', value: boolean): void
  (e: 'update:loading', value: boolean): void
  (e: 'update:measurementLoading', value: boolean): void
  (e: 'open-virtual-param'): void
  (e: 'clearSelectedVirtualParameter'): void
  (e: 'toggle-fault-mode', value: boolean): void
  (e: 'open-export-data'): void
  (e: 'open-similarity-search'): void
  (e: 'parametersRemoved', value: string[]): void
  (e: 'update:isMeasuring', value: boolean): void
}>()

// 控制是否显示全景图
const isPreview = ref(false)
// 显示视图设置模态框的状态
const showViewSettingsModal = ref(false)
// 保存视图设置的临时副本，用于取消操作时还原
const tempViewSettings = ref<Record<string, ParameterViewSetting> | null>(null)
// 控制是否忽略用户设置的Y轴范围
const skipUserYAxisSettings = ref(false)

// 打开视图设置对话框
const openViewSettings = () => {
  // 保存当前视图设置的深拷贝
  tempViewSettings.value = JSON.parse(JSON.stringify(props.viewSettings || {}))
  showViewSettingsModal.value = true
}

// 取消视图设置并关闭对话框
const cancelViewSettings = () => {
  // 还原为打开对话框前的设置
  if (tempViewSettings.value && props.viewSettings) {
    emit('update:viewSettings', tempViewSettings.value)
  }
  showViewSettingsModal.value = false
}

// 保存视图设置并更新图表
const saveViewSettings = async (isChangeTask: boolean = false) => {
  showViewSettingsModal.value = false
  // 确保视图设置已经生效，不需要再更新一次（已经实时更新）
  if (props.viewSettings) {
    // 计算离散区域的实际高度，根据显示的通道个数动态变化

    // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
    const selectedParams = selectedParametersList.value?.filter(param => 
      param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
    ) || []
    if (selectedParams.length === 0) {
      discreteAreaHeight.value = 0
    } // 如果没有选中参数，高度为0
    
    // 计算有多少个显示为离散模式的参数及通道
    let totalDiscreteChannels = 0
    const discreteParamCount = selectedParams.filter(name => {
      // 检查此参数的离散通道数量
      const discreteChannels = ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].filter(channelId => {
        return getChannelSettingDisplayMode(name, channelId) === 'discrete' && 
              getChannelSettingVisible(name, channelId)
      })
      
      // 累计通道数
      totalDiscreteChannels += discreteChannels.length
      
      // 如果该参数有任何通道显示为离散，则计入参数数
      return discreteChannels.length > 0
    }).length
    
    if (discreteParamCount === 0 || totalDiscreteChannels === 0) {
      discreteAreaHeight.value = 0
    } // 如果没有离散参数或通道，高度为0
    
    // 计算应该的高度：每个通道固定高度 * 通道总数 + 参数间间距
    const paramSpacing = 0 // 参数之间的间距
    const calculatedHeight = baseChannelHeight * totalDiscreteChannels + (discreteParamCount - 1) * paramSpacing
    
    // 限制最大高度
    discreteAreaHeight.value = Math.min(calculatedHeight, maxDiscreteAreaHeight)

    // 水平缩放下，保存当前的Y轴范围
    if (isRectZoomActive.value || zoomType.value === 'horizontal') {
      // 保存每个参数的当前Y轴范围
      selectedParams.forEach(paramName => {
        if (getParameterDisplayMode(paramName) === 'continuous') {
          // 检查是否有用户设置的Y轴范围
          const userYAxisMin = getParameterYAxisMin(paramName);
          const userYAxisMax = getParameterYAxisMax(paramName);
          // 如果用户设置了Y轴范围，更新rectZoomYRanges中的值
          if (userYAxisMin !== undefined || userYAxisMax !== undefined) {
            // 获取当前参数的Y轴范围
            let currentRange = rectZoomYRanges.value[paramName];
            
            // 如果rectZoomYRanges中没有该参数的范围，尝试从paramYScaleMap获取实际的Y轴范围
            if (!currentRange && paramYScaleMap.value[paramName]) {
              const paramYScale = paramYScaleMap.value[paramName];
              const domain = paramYScale.domain();
              currentRange = [domain[0], domain[1]];
            }
            
            // 如果仍然没有范围，使用默认值
            if (!currentRange) {
              let computedMin = Infinity
              let computedMax = -Infinity
              let hasData = false
              // 计算该参数在所有可见通道中的数据范围
              defaultData.value.forEach((item) => {
                if (item.parameters[paramName]) {
                  const dataMin = Math.min(...item.parameters[paramName].map(d => d.value))
                  const dataMax = Math.max(...item.parameters[paramName].map(d => d.value))
                  computedMin = Math.min(computedMin, dataMin)
                  computedMax = Math.max(computedMax, dataMax)
                  hasData = true
                }
              })
              // 如果有数据，扩展范围
              if (hasData) {
                const valueRange = computedMax - computedMin
                const paddingFactor = 0.2
                const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
                computedMin -= padding / 2
                computedMax += padding / 2
              }
    
              currentRange = [computedMin, computedMax];
            }
            
            // 更新Y轴范围，只更新用户设置了的部分
            const newRange: [number, number] = [
              userYAxisMin !== undefined ? userYAxisMin : currentRange[0],
              userYAxisMax !== undefined ? userYAxisMax : currentRange[1]
            ];
            
            // 确保新范围有效(min < max)
            if (newRange[0] >= newRange[1]) {
              console.warn(`参数 ${paramName} 的Y轴范围设置有误 (${newRange[0]} >= ${newRange[1]})，已调整`);
              // 调整范围，确保min < max
              const middle = (newRange[0] + newRange[1]) / 2;
              newRange[0] = middle - 1;
              newRange[1] = middle + 1;
            }
            
            // 更新这个参数的Y轴范围
            rectZoomYRanges.value[paramName] = newRange;
            
            // 设置矩形缩放活动标志
            isRectZoomActive.value = true;
          }
        }
      });
    }
    // 强制重新绘制图表以应用新设置
    nextTick(() => {
      if (isChangeTask) {
        // 如果是回放任务，全部使用视图设置
        updateChart(false)
      } else {
        // 如果在水平缩放模式下，使用保持Y轴范围的更新方法
        if (isRectZoomActive.value) {
          updateChartWithParamYScales();
        } else {
          updateChart(false);
        }
      }
      
    })
    
    console.log('视图设置已保存并更新图表显示')
  }
}

// 引用容器元素
const dataViewerContainer = ref<HTMLElement | null>(null)
const chartContainer = ref<HTMLElement | null>(null)
// 用于跟踪高亮线条的状态
const highlightedLines = ref<{paramId: string, channelId: string, originalWidth: number}[]>([])

// 处理从ParameterValueDisplay组件发送过来的highlightLine事件
const handleHighlightLine = (paramId: string, channelId: string) => {
  if (!svg) return
  
  // 构建线条选择器
  const lineSelector = `.line-${escapeSelector(paramId)}-${channelId}`
  const line = svg.select(lineSelector)
  
  if (!line.empty()) {
    // 检查该线条是否已经被高亮
    const isAlreadyHighlighted = highlightedLines.value.some(
      item => item.paramId === paramId && item.channelId === channelId
    )
    
    if (isAlreadyHighlighted) {
      // 如果已经高亮，则取消高亮
      const highlightItem = highlightedLines.value.find(
        item => item.paramId === paramId && item.channelId === channelId
      )
      
      if (highlightItem) {
        // 恢复原始宽度
        line.attr('stroke-width', highlightItem.originalWidth)
        
        // 从高亮数组中移除
        highlightedLines.value = highlightedLines.value.filter(
          item => !(item.paramId === paramId && item.channelId === channelId)
        )
      }
    } else {
      // 如果未高亮，则添加高亮
      // 获取原始线条宽度
      const originalWidth = parseFloat(line.attr('stroke-width') || '1')
      
      // 保存高亮状态信息
      highlightedLines.value.push({
        paramId,
        channelId,
        originalWidth
      })
      
      // 应用加粗效果 - 将线条宽度增加3倍或最小4px
      const highlightWidth = Math.max(originalWidth * 3, 4)
      line.attr('stroke-width', highlightWidth)
    }
  }
}

// 处理从ParameterValueDisplay组件发送过来的highlightAllChannels事件
const handleHighlightAllChannels = (paramId: string) => {
  if (!svg) return
  
  // 获取所有通道ID
  // 获取当前有数据的所有通道（只包含有该参数数据的通道）
  const allChannels: string[] = []
  if (defaultData.value && Array.isArray(defaultData.value)) {
    defaultData.value.forEach(channel => {
      if (
        channel &&
        channel.channelId &&
        channel.parameters &&
        channel.parameters[paramId] &&
        Array.isArray(channel.parameters[paramId]) &&
        channel.parameters[paramId].length > 0
      ) {
        allChannels.push(channel.channelId)
      }
    })
  }
  
  // 检查该参数是否有通道
  const allChannelsHighlighted = allChannels.every(channelId => 
    highlightedLines.value.some(
      item => item.paramId === paramId && item.channelId === channelId
    )
  )
  
  if (allChannelsHighlighted) {
    // 如果所有通道都已高亮，则取消所有通道的高亮
    allChannels.forEach(channelId => {
      const lineSelector = `.line-${escapeSelector(paramId)}-${channelId}`
      const line = svg.select(lineSelector)
      
      if (!line.empty()) {
        const highlightItem = highlightedLines.value.find(
          item => item.paramId === paramId && item.channelId === channelId
        )
        
        if (highlightItem) {
          // 恢复原始宽度
          line.attr('stroke-width', highlightItem.originalWidth)
        }
      }
    })
    
    // 从高亮数组中移除该参数的所有线条
    highlightedLines.value = highlightedLines.value.filter(
      item => item.paramId !== paramId
    )
  } else {
    // 如果未全部高亮，则高亮该参数在所有通道的曲线
    allChannels.forEach(channelId => {
      const lineSelector = `.line-${escapeSelector(paramId)}-${channelId}`
      const line = svg.select(lineSelector)
      
      if (!line.empty()) {
        // 检查该通道是否已经被高亮
        const isAlreadyHighlighted = highlightedLines.value.some(
          item => item.paramId === paramId && item.channelId === channelId
        )
        
        if (!isAlreadyHighlighted) {
          // 获取原始线条宽度
          const originalWidth = parseFloat(line.attr('stroke-width') || '1')
          
          // 保存高亮状态信息
          highlightedLines.value.push({
            paramId,
            channelId,
            originalWidth
          })
          
          // 应用加粗效果 - 将线条宽度增加3倍或最小4px
          const highlightWidth = Math.max(originalWidth * 3, 4)
          line.attr('stroke-width', highlightWidth)
        }
      }
    })
  }
}

// 用于跟踪标注高亮的状态
const annotationHighlights = ref<{
  paramName: string
  channelId: string
  originalWidth: number
  similarityDataEndTime: number
  similarityDataStartTime: number
  highlightPath: d3.Selection<SVGPathElement, unknown, null, undefined> | null
}[]>([])

// 更新标注显示 - 根据标注列表加粗显示对应的曲线段
const updateAnnotate = (annotations: Array<{
  paramName: string
  fileName: string
  similarityDataEndTime: string
  similarityDataStartTime: string
}>) => {
  if (!svg) return

  // 应用新的标注高亮
  annotations.forEach(annotation => {
    // 根据fileName在props.selectedChannels中获取channelId
    const channelId = props.selectedChannel.find(channel => channel.fileName === annotation.fileName)?.channelId
    const { paramName, similarityDataStartTime, similarityDataEndTime } = annotation
    // 如果annotationHighlights中存在相同的标注，则跳过
    if (annotationHighlights.value.some(highlight => highlight.paramName === paramName && highlight.channelId === channelId && highlight.similarityDataStartTime == similarityDataStartTime && highlight.similarityDataEndTime == similarityDataEndTime)) {
      return
    }

    // 构建线条选择器
    const lineSelector = `.line-${escapeSelector(paramName)}-${channelId}`
    const line = svg.select(lineSelector)

    if (!line.empty()) {
      // 获取原始线条宽度
      const originalWidth = parseFloat(line.attr('stroke-width') || '1')

      // 创建高亮路径 - 只显示指定时间段的曲线
      const highlightPath = createHighlightPath(paramName, channelId, parseFloat(similarityDataStartTime), parseFloat(similarityDataEndTime))

      if (highlightPath) {
        // 保存标注高亮状态信息
        annotationHighlights.value.push({
          paramName,
          channelId,
          originalWidth,
          similarityDataStartTime: parseFloat(similarityDataStartTime),
          similarityDataEndTime: parseFloat(similarityDataEndTime),
          highlightPath
        })
      }
    }
  })
}

// 创建高亮路径 - 只显示指定时间范围的曲线
const createHighlightPath = (paramName: string, channelId: string, similarityDataStartTime: number, similarityDataEndTime: number) => {
  if (!svg || !defaultData.value) return null

  // 找到对应通道的数据
  const channelData = defaultData.value.find((channel: any) => channel.channelId === channelId)
  if (!channelData || !channelData.parameters || !channelData.parameters[paramName]) {
    return null
  }

  // 获取参数数据
  const paramData = channelData.parameters[paramName]
  if (!Array.isArray(paramData) || paramData.length === 0) {
    return null
  }

  // 过滤出指定时间范围内的数据点
  const filteredData = paramData.filter((point: any) => {
    const timestamp = new Date(point.timestamp).getTime()
    return timestamp >= similarityDataStartTime && timestamp <= similarityDataEndTime
  })

  if (filteredData.length === 0) return null

  // 创建路径数据
  const pathData = filteredData.map((point: any) => {
    const timestamp = new Date(point.timestamp).getTime()
    const x = xScale(timestamp)
    // 优先使用参数专属的y轴比例尺
    const paramYScale = paramYScaleMap.value && paramYScaleMap.value[paramName] ? paramYScaleMap.value[paramName] : yScale
    const y = paramYScale(point.value)
    return [x, y]
  })

  // 生成路径字符串
  const pathString = generatePathString(pathData)

  // 创建高亮路径元素
  const highlightPath = svg.append('path')
    .attr('class', `highlight-line-${escapeSelector(paramName)}-${channelId}`)
    .attr('d', pathString)
    .attr('fill', 'none')
    .attr('stroke', getChannelSettingColor(paramName, channelId))
    .attr('stroke-width', Math.max(parseFloat(svg.select(`.line-${escapeSelector(paramName)}-${channelId}`).attr('stroke-width') || '1') * 3, 4))
    .attr('stroke-linecap', 'round')
    .attr('stroke-linejoin', 'round')
    .style('pointer-events', 'none')
    .style('z-index', '10')

  return highlightPath
}

// 生成路径字符串
const generatePathString = (points: [number, number][]) => {
  if (points.length === 0) return ''

  let pathString = `M ${points[0][0]} ${points[0][1]}`

  for (let i = 1; i < points.length; i++) {
    pathString += ` L ${points[i][0]} ${points[i][1]}`
  }

  return pathString
}

// 更新所有标注高亮路径 - 在缩放时调用
const updateAnnotationHighlights = () => {
  if (!svg) return

  annotationHighlights.value.forEach(highlight => {
    if (highlight.highlightPath) {
      // 重新计算路径数据
      const pathData = recalculateHighlightPathData(
        highlight.paramName,
        highlight.channelId,
        highlight.similarityDataStartTime,
        highlight.similarityDataEndTime
      )

      if (pathData.length > 0) {
        const pathString = generatePathString(pathData)
        highlight.highlightPath.attr('d', pathString)
        highlight.highlightPath.attr('clip-path', 'url(#chart-area)')

        highlight.highlightPath.style('display', 'block')
      } else {
        // 先隐藏
        highlight.highlightPath.style('display', 'none')
      }
    }
  })
}

// 重新计算高亮路径数据
const recalculateHighlightPathData = (paramName: string, channelId: string, similarityDataStartTime: number, similarityDataEndTime: number) => {
  if (!defaultData.value) return []

  // 找到对应通道的数据
  const channelData = defaultData.value.find((channel: any) => channel.channelId === channelId)
  if (!channelData || !channelData.parameters || !channelData.parameters[paramName]) {
    return []
  }

  // 获取参数数据
  const paramData = channelData.parameters[paramName]
  if (!Array.isArray(paramData) || paramData.length === 0) {
    return []
  }

  // 过滤出指定时间范围内的数据点
  const filteredData = paramData.filter((point: any) => {
    const timestamp = new Date(point.timestamp).getTime()
    return timestamp >= similarityDataStartTime && timestamp <= similarityDataEndTime
  })

  if (filteredData.length === 0) return []

  // 创建路径数据
  return filteredData.map((point: any) => {
    const timestamp = new Date(point.timestamp).getTime()
    const x = xScale(timestamp)
    // 优先使用参数专属的y轴比例尺
    const paramYScale = paramYScaleMap.value && paramYScaleMap.value[paramName] ? paramYScaleMap.value[paramName] : yScale
    const y = paramYScale(point.value)
    return [x, y]
  })
}

// 清除标注高亮
const clearAnnotationHighlights = (annotations: Array<{
  paramName: string
  fileName: string
  similarityDataStartTime: string
  similarityDataEndTime: string
}>) => {
  if (!svg) return

  annotations.forEach(annotation => {
    // 根据fileName在props.selectedChannels中获取channelId
    const channelId = props.selectedChannel.find(channel => channel.fileName === annotation.fileName)?.channelId
    const { paramName, similarityDataStartTime, similarityDataEndTime } = annotation
    // 移除高亮路径
    const highlightPath = annotationHighlights.value.find(highlight => highlight.paramName === paramName && highlight.channelId === channelId && highlight.similarityDataStartTime == similarityDataStartTime && highlight.similarityDataEndTime == similarityDataEndTime)?.highlightPath
    if (highlightPath) {
      highlightPath.remove()
      // 从annotationHighlights中移除该标注
      annotationHighlights.value = annotationHighlights.value.filter(highlight => highlight.paramName !== paramName || highlight.channelId !== channelId || highlight.similarityDataStartTime != similarityDataStartTime || highlight.similarityDataEndTime != similarityDataEndTime)
    }
  })
  // 清空选定标注高亮数组
  annotationHighlights.value = annotationHighlights.value.filter(highlight => !annotations.some(annotation => annotation.paramName === highlight.paramName && annotation.channelId === highlight.channelId && annotation.startTime == highlight.startTime && annotation.endTime == highlight.endTime))
}

// 清除所有标注高亮
const clearAllAnnotationHighlights = () => {
  if (!svg) return
  annotationHighlights.value.forEach(highlight => {
    if (highlight.highlightPath) {
      highlight.highlightPath.remove()
    }
  })
  annotationHighlights.value = []
}

let svg: d3.Selection<SVGSVGElement, unknown, null, undefined>
let xScale: d3.ScaleLinear<number, number> | d3.ScaleTime<number, number>
let yScale: d3.ScaleLinear<number, number>
const margin = { top: 20, right: 20, bottom: 30, left: 60 }

// 添加故障模式相关的状态变量
const currentSelectedFaultIds = ref<string[]>([])
const currentFaultList = ref<any[]>([])
const updateFaultArea = ref(false)
// 添加故障色带高度相关变量
const faultBandHeight = 15 // 每个故障色带的高度
const faultBandSpacing = 0 // 故障色带之间的间距
const faultBandsAreaHeight = ref(0) // 故障色带区域的总高度

// 计算故障色带区域的总高度
const calculateFaultBandsHeight = (selectedFaultIds: string[]) => {
  if (!selectedFaultIds || selectedFaultIds.length === 0) {
    return 0
  }
  return selectedFaultIds.length * faultBandHeight + (selectedFaultIds.length - 1) * faultBandSpacing + 10 // 10px为与开关量色带的间距
}

// 更新故障色带区域高度
const updateFaultBandsHeight = (selectedFaultIds: string[]) => {
  faultBandsAreaHeight.value = calculateFaultBandsHeight(selectedFaultIds)
}

const initialDomain = ref<Array<number | Date>>([Infinity, -Infinity])
const initialYDomain = [0, 100] // 保存初始y轴范围
let dragStartX = 0 // 拖动开始位置
let dragEndX = 0 // 拖动结束位置
let dragStartY = 0 // 垂直拖动开始位置
let dragEndY = 0 // 垂直拖动结束位置
const isDragging = ref(false)
// 将interactionMode的初始值设为null，不再有特定模式
const interactionMode = ref<'measure' | 'polygon' | null>(null) // 初始值为null，只保留测量和多边形模式
const zoomType = ref<'horizontal' | 'rect' | 'vertical'>('horizontal') // 默认为水平缩放
const isMeasuring = ref(false) // 是否处于测量状态
const activeCursor = ref<'cursor1' | 'cursor2'>('cursor1') // 当前激活的游标
const cursor1Position = ref<number | Date>(0) // 游标1位置，初始值将在createChart时设置
const cursor2Position = ref<number | Date>(0) // 游标2位置，初始值将在createChart时设置
const currentCursorPosition = ref(0) // 普通模式下的当前游标位置

// 游标标签相关状态
// 移除全局的showCursorLabels，改为基于参数设置显示标签
interface CursorLabel {
  id: string
  paramName: string
  channelId: string
  x: number
  y: number
  color: string
  text: string
  isDragging: boolean
  offsetX: number // 相对于游标点的X偏移
  offsetY: number // 相对于游标点的Y偏移
}
const cursorLabels = ref<CursorLabel[]>([]) // 游标标签数组
const isDraggingLabel = ref(false) // 是否正在拖动标签
const draggingLabelId = ref<string | null>(null) // 当前拖动的标签ID

// 多边形相关状态和数据
const polygonPoints = ref<{x: number, y: number, dataX: number, dataY: number}[]>([])
const currentPolygon = ref<d3.Selection<SVGPolygonElement, unknown, null, undefined> | null>(null)
const tempLine = ref<d3.Selection<SVGLineElement, unknown, null, undefined> | null>(null)
const showPolygon = ref(false)
const showPolygonData = ref(false)

// 多边形表格列定义
const polygonColumns = [
  { title: '序号', dataIndex: 'index', key: 'index' },
  { title: 'X轴(时间)', dataIndex: 'dataX', key: 'dataX' },
  { title: 'Y轴(数值)', dataIndex: 'dataY', key: 'dataY' }
]

// 多边形表格数据
const polygonTableData = computed(() => {
  return polygonPoints.value.map((point, index) => ({
    key: index,
    index: index + 1,
    dataX: point.dataX.toFixed(3),
    dataY: point.dataY.toFixed(3)
  }))
})

// 添加离散量区域高度
const baseChannelHeight = 20 // 单个通道的基础高度
const maxDiscreteAreaHeight = 150 // 最大高度限制
const maxFaultBandsAreaHeight = 150 // 最大高度限制
// 计算离散区域的实际高度，根据显示的通道个数动态变化
const discreteAreaHeight = ref(0)

// 添加一个常量定义缩放带的高度
const zoomBarHeight = ref(40)

// 添加Y轴模式状态变量
const useSharedYAxis = ref(false) // 默认使用独立Y轴模式

const defaultData = ref<any>([])
const outViewData = ref<any>([])

// 工具下拉菜单相关状态
const showToolsDropdown = ref(false)
const toolsDropdown = ref<HTMLElement | null>(null)
const isFaultModeActive = ref(false)

// 添加loading状态变量
const isLoading = ref(false)
// 添加测量数据loading状态变量
const isMeasurementLoading = ref(false)

// 添加一个计算缩放带位置的函数
const getZoomBarY = () => {
  // 计算缩放带应该在的垂直位置
  // 主图表高度 + 离散区域高度 + 5px间距
  const containerHeight = chartContainer.value ? chartContainer.value.clientHeight : 0
  
  // 如果离散区域高度为0，不添加额外间距
  const discreteHeight = discreteAreaHeight.value
  const spacing = discreteHeight > 0 ? 5 : 0

  const faultBandsHeight = faultBandsAreaHeight.value
  
  const mainChartHeight = containerHeight - discreteHeight - faultBandsHeight - zoomBarHeight.value
  return mainChartHeight + discreteHeight + faultBandsHeight + spacing
}

// Y轴模式切换函数
const toggleYAxisMode = () => {
  // 切换Y轴模式
  useSharedYAxis.value = !useSharedYAxis.value
  
  console.log('切换Y轴模式:', useSharedYAxis.value ? '共享Y轴' : '独立Y轴')
  
  // 更新图表显示
  nextTick(() => {
    if (isRectZoomActive.value) {
      updateChartWithParamYScales()
    } else {
      updateChart(false)
    }
  })
}

//去除虚拟参数前缀
const getShowName = (paramName: string): string => {
  if (paramName.startsWith('V_')) {
    return paramName.substring(2) // 去除"V_"前缀
  }
  return paramName
}

const initChart = () => {
  if (!chartContainer.value) return

  const width = chartContainer.value.clientWidth
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value // 减去离散量区域和缩放带高度
  
  // 清除现有SVG
  d3.select(chartContainer.value).selectAll('*').remove()

  // 创建SVG元素
  svg = d3.select(chartContainer.value)
    .append('svg')
    .attr('width', '100%')
    .attr('height', '100%')
    .attr('viewBox', `0 0 ${width} ${height + discreteAreaHeight.value + faultBandsAreaHeight.value + zoomBarHeight.value}`) // 增加视口高度
    .attr('preserveAspectRatio', 'xMidYMid meet')

  // 创建坐标轴
  xScale = d3.scaleTime()
    .domain(initialDomain.value as [Date, Date])
    .range([margin.left, width - margin.right])

  // 初始化yScale，后续会根据数据动态调整
  yScale = d3.scaleLinear()
    .domain(initialYDomain)
    .range([height - margin.bottom, margin.top]) // 调整Y轴范围，不包括离散量和缩放带
    
  // 添加裁剪区域，确保曲线不超出坐标轴范围
  svg.append('defs')
    .append('clipPath')
    .attr('id', 'chart-area')
    .append('rect')
    .attr('x', margin.left)
    .attr('y', margin.top)
    .attr('width', width - margin.left - margin.right)
    .attr('height', height - margin.top - margin.bottom) // 只考虑主图表区域
  
  // 添加裁剪区域，专门用于缩放带
  svg.append('defs')
    .append('clipPath')
    .attr('id', 'zoom-bar-area')
    .append('rect')
    .attr('x', margin.left)
    .attr('y', getZoomBarY()) // 使用函数计算正确位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10) // 缩放带高度

  // 添加曲线区域的坐标轴
  const xAxisGroup = svg.append('g')
    .attr('class', 'axis x-axis')
    .attr('transform', `translate(0,${height - margin.bottom})`)
    
  // RJ模式：使用时间格式化
  xAxisGroup.call(d3.axisBottom(xScale as d3.ScaleTime<number, number>).tickFormat(d3.timeFormat('%Y-%m-%d %H:%M:%S')).ticks(5));

  // 添加Y轴 - 明确设置长度，使其不会延伸到色带区域
  const yAxis = d3.axisLeft(yScale).tickFormat(formatAxisNumber)
  const yAxisGroup = svg.append('g')
    .attr('class', 'axis y-axis initial-y-axis')
    .attr('transform', `translate(${margin.left},0)`)
    .call(yAxis)
  
  // 手动修正Y轴刻度线的长度，确保不会延伸超出主图表区域
  yAxisGroup.selectAll('.tick line')
    .attr('x2', -6) // 刻度线长度
  
  // 为Y轴添加矩形背景，限制其高度
  svg.insert('rect', '.y-axis')
    .attr('class', 'y-axis-background')
    .attr('x', margin.left - 60) // 给Y轴标签留出足够空间
    .attr('y', margin.top)
    .attr('width', 60) // Y轴区域的宽度
    .attr('height', height - margin.top - margin.bottom)
    .attr('fill', 'transparent')

  // 创建离散量区域的组，放在主图表下方
  const discreteGroup = svg.append('g')
    .attr('class', 'discrete-group')
    .attr('transform', `translate(0, ${height})`)
  
  // 为离散量添加背景
  discreteGroup.append('rect')
    .attr('class', 'discrete-background')
    .attr('x', margin.left)
    .attr('y', 0)
    .attr('width', width - margin.left - margin.right)
    .attr('height', discreteAreaHeight.value)
    .attr('fill', isDarkMode.value ? '#1e1e1e' : '#f5f5f5')
    .attr('stroke', isDarkMode.value ? '#444444' : '#e0e0e0')
    .attr('stroke-width', 1)
    
  // 创建缩放带区域的组，放在离散量区域下方
  const zoomBarGroup = svg.append('g')
    .attr('class', 'zoom-bar-group')
    .attr('transform', `translate(0, ${getZoomBarY()})`) // 使用函数计算正确位置

  // 添加"概览"标签
  zoomBarGroup.append('text')
    .attr('class', 'overview-label')
    .attr('x', margin.left + 10)
    .attr('y', 15)
    .attr('font-size', '12px')
    .attr('fill', '#1890ff')
    .text('概览')
  
  // 为缩放带添加背景
  zoomBarGroup.append('rect')
    .attr('class', 'zoom-bar-background')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', 'none')
    .attr('pointer-events', 'all')
    .style('cursor', 'grab')

  // 在缩放带上显示整体数据概览
  updateOverviewChart(zoomBarGroup, width, height)

    // 添加游标线，初始化在x=0位置，并应用裁剪
  const cursorGroup = svg.append('g')
    .attr('class', 'cursor-group')
    .attr('clip-path', 'url(#chart-area)')
  
  const initialX = xScale(0);
  
  // 变量保存当前游标X坐标，方便后续引用
  let currentCursorX = initialX;

  cursorGroup.append('line')
    .attr('class', 'cursor-line')
    .attr('x1', initialX)
    .attr('x2', initialX)
    .attr('y1', margin.top)
    .attr('y2', chartContainer.value!.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) // 只延伸到主图表区域
    .attr('stroke', '#1890ff') // 使用主题蓝色
    .attr('stroke-width', 1)
    .style('display', 'block') // 初始时显示游标
    .style('cursor', 'move') // 鼠标样式为可移动
    .on('mouseover', function() {
      // 鼠标悬停时改变游标样式
      d3.select(this)
        .attr('stroke-width', 2)
        .attr('stroke', '#3b82f6')
    })
    .on('mouseout', function() {
      // 鼠标离开时恢复游标样式
      d3.select(this)
        .attr('stroke-width', 1)
        .attr('stroke', 'var(--text-color)')
    })
    // 添加游标拖动功能
    .on('mousedown', function(event) {
      event.stopPropagation()
      isDragging.value = true
      currentCursorX = parseFloat(d3.select(this).attr('x1')); // 更新当前X坐标
    })

  // 添加游标手柄图标
  const addCursorHandle = (curX: number) => {
    cursorGroup.append('rect')
      .attr('class', 'cursor-handle')
      .attr('x', curX - 8)
      .attr('y', margin.top + 30)
      .attr('width', 16)
      .attr('height', 25)
      .attr('fill', '#3b82f6')
      .attr('stroke', '#1e40af')
      .attr('stroke-width', 1.5)
      .attr('rx', 4)
      .attr('ry', 4)
      .style('cursor', 'move')
      .style('display', 'block')
    .on('mouseover', function() {
      d3.select(this)
        .attr('fill', '#2563eb')
        .attr('stroke-width', 2.5)
        .attr('width', 18)
          .attr('x', curX - 9)
          .attr('height', 27)
    })
    .on('mouseout', function() {
      d3.select(this)
        .attr('fill', '#3b82f6')
        .attr('stroke-width', 1.5)
        .attr('width', 16)
          .attr('x', curX - 8)
          .attr('height', 25)
    })
    .on('mousedown', function(event) {
      event.stopPropagation()
      isDragging.value = true
    })
  }
  
  // 添加游标手柄
  addCursorHandle(initialX);

  // 为鼠标事件添加一个透明层，扩展到整个图表区域
  svg.append('rect')
    .attr('class', 'overlay')
    .attr('width', width - margin.left - margin.right + 40) // 右边扩展10px，游标可移动到最右
    .attr('height', height - margin.top - margin.bottom) // 只考虑主图表区域
    .attr('x', margin.left - 30) // 左边扩展30px，防止鼠标从y轴左侧开始移动时，无法触发缩放
    .attr('y', margin.top)
    .attr('fill', 'none')
    .attr('pointer-events', 'all')
    .on('mousedown', handleMouseDown)
    .on('mousemove', handleMouseMove)
    .on('mouseup', handleMouseUp) // 添加mouseup事件处理
    .on('mouseleave', handleMouseLeave)

    // 添加缩放带的交互区域
  const zoomBarOverlay = zoomBarGroup.append('rect')
    .attr('class', 'zoom-bar-overlay')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', 'none')
    .attr('pointer-events', 'all')
  
  // 添加可拖动的视窗指示器
  const zoomWindow = zoomBarGroup.append('rect')
    .attr('class', 'zoom-window')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', 'rgba(173, 216, 230, 0.4)')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
    .style('pointer-events', 'none') // 禁用交互
    
  // 添加缩放带的拖动区域句柄
  const zoomHandleLeft = zoomBarGroup.append('rect')
    .attr('class', 'zoom-handle zoom-handle-left')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', 4)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', '#1890ff')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
    .style('pointer-events', 'none') // 禁用交互
    
  const zoomHandleRight = zoomBarGroup.append('rect')
    .attr('class', 'zoom-handle zoom-handle-right')
    .attr('x', width - margin.right - 4)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', 4)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', '#1890ff')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
    .style('pointer-events', 'none') // 禁用交互
  
  // 为缩放带添加拖动事件
  setupZoomBarInteractions(zoomBarOverlay, zoomWindow, zoomHandleLeft, zoomHandleRight)

  // 初始化图表数据
  updateChart(false)
  
  // 更新离散量显示
  updateDiscreteArea()
}

// 设置缩放带交互
const setupZoomBarInteractions = (
  zoomBarOverlay: d3.Selection<SVGRectElement, unknown, null, undefined>, 
  zoomWindow: d3.Selection<SVGRectElement, unknown, null, undefined>, 
  zoomHandleLeft: d3.Selection<SVGRectElement, unknown, null, undefined>, 
  zoomHandleRight: d3.Selection<SVGRectElement, unknown, null, undefined>
) => {
  if (!chartContainer.value) return
  
  const width = chartContainer.value.clientWidth
  const zoomBarWidth = width - margin.left - margin.right

  // 拖动状态变量
  let isDragging = false
  let dragType: 'left' | 'right' | 'window' | null = null
  let startX = 0
  let startLeft = 0
  let startWidth = 0

  // 启用手柄的交互
  zoomHandleLeft.style('pointer-events', 'all')
  zoomHandleRight.style('pointer-events', 'all')

  // 左手柄拖动事件
  zoomHandleLeft
    .on('mousedown', function(event: MouseEvent) {
      event.preventDefault()
      isDragging = true
      dragType = 'left'
      startX = event.clientX
      startLeft = parseFloat(zoomWindow.attr('x') || '0')
      startWidth = parseFloat(zoomWindow.attr('width') || '0')

      // 添加拖动样式
      d3.select(this).style('cursor', 'ew-resize')

      // 绑定全局鼠标事件
      d3.select('body')
        .on('mousemove.zoombar', handleMouseMove)
        .on('mouseup.zoombar', handleMouseUp)
        .on('mouseleave.zoombar', handleMouseUp)
    })

  // 右手柄拖动事件
  zoomHandleRight
    .on('mousedown', function(event: MouseEvent) {
      event.preventDefault()
      isDragging = true
      dragType = 'right'
      startX = event.clientX
      startLeft = parseFloat(zoomWindow.attr('x') || '0')
      startWidth = parseFloat(zoomWindow.attr('width') || '0')

      // 添加拖动样式
      d3.select(this).style('cursor', 'ew-resize')

      // 绑定全局鼠标事件
      d3.select('body')
        .on('mousemove.zoombar', handleMouseMove)
        .on('mouseup.zoombar', handleMouseUp)
        .on('mouseleave.zoombar', handleMouseUp)
    })

  // 缩放窗口拖动事件（保持长度不变）
  zoomWindow
    .on('mousedown', function(event: MouseEvent) {
      event.preventDefault()
      isDragging = true
      dragType = 'window'
      startX = event.clientX
      startLeft = parseFloat(zoomWindow.attr('x') || '0')
      startWidth = parseFloat(zoomWindow.attr('width') || '0')

      // 添加拖动样式
      d3.select(this).style('cursor', 'move')

      // 绑定全局鼠标事件
      d3.select('body')
        .on('mousemove.zoombar', handleMouseMove)
        .on('mouseup.zoombar', handleMouseUp)
        .on('mouseleave.zoombar', handleMouseUp)
    })

  // 鼠标移动处理
  const handleMouseMove = (event: MouseEvent) => {
    if (!isDragging || !chartContainer.value) return

    const deltaX = event.clientX - startX
    let newLeft = startLeft
    let newWidth = startWidth

    switch (dragType) {
      case 'left':
        // 拖动左手柄，调整左边界和宽度
        newLeft = Math.max(margin.left, startLeft + deltaX)
        newWidth = Math.max(20, startWidth - (newLeft - startLeft))
        break

      case 'right':
        // 拖动右手柄，调整右边界和宽度
        newWidth = Math.max(20, startWidth + deltaX)
        newLeft = startLeft
        break

      case 'window':
        // 拖动整个窗口，保持宽度不变
        newLeft = Math.max(margin.left, Math.min(margin.left + zoomBarWidth - startWidth, startLeft + deltaX))
        newWidth = startWidth
        break
    }

    // 确保不超出边界
    if (newLeft + newWidth > margin.left + zoomBarWidth) {
      newWidth = margin.left + zoomBarWidth - newLeft
    }

    // 更新缩放窗口位置
    zoomWindow
      .attr('x', newLeft)
      .attr('width', newWidth)

    // 更新手柄位置
    zoomHandleLeft.attr('x', newLeft)
    zoomHandleRight.attr('x', newLeft + newWidth - 4)
  }

  // 鼠标松开处理
  const handleMouseUp = () => {
    if (!isDragging) return

    isDragging = false
    dragType = null

    // 移除拖动样式
    zoomHandleLeft.style('cursor', 'ew-resize')
    zoomHandleRight.style('cursor', 'ew-resize')
    zoomWindow.style('cursor', 'move')

    // 解绑全局鼠标事件
    d3.select('body')
      .on('mousemove.zoombar', null)
      .on('mouseup.zoombar', null)
      .on('mouseleave.zoombar', null)

    // 根据新的缩放窗口位置更新X轴范围并查询数据
    updateXAxisFromZoomWindow()
  }

  // 根据缩放窗口位置更新X轴范围
  const updateXAxisFromZoomWindow = () => {
    if (!chartContainer.value) return

    const zoomWindowLeft = parseFloat(zoomWindow.attr('x') || '0')
    const zoomWindowWidth = parseFloat(zoomWindow.attr('width') || '0')

    // 计算缩放窗口在缩放带中的位置比例
    const leftRatio = (zoomWindowLeft - margin.left) / zoomBarWidth
    const rightRatio = (zoomWindowLeft + zoomWindowWidth - margin.left) / zoomBarWidth

    // 获取缩放带的初始范围（全局数据范围）
    const [zoomBarMin, zoomBarMax] = initialDomain.value

    // 计算新的X轴范围
    let newMin: number | Date
    let newMax: number | Date

    const totalTimeRange = (zoomBarMax as Date).getTime() - (zoomBarMin as Date).getTime()
    newMin = new Date((zoomBarMin as Date).getTime() + leftRatio * totalTimeRange)
    newMax = new Date((zoomBarMin as Date).getTime() + rightRatio * totalTimeRange)

    // 更新X轴范围
    xScale.domain([newMin, newMax])

    // 更新X轴显示
    if (svg) {
      svg.select('.x-axis').call(d3.axisBottom(xScale) as any)
    }

    // 查询新范围的数据并更新图表
    queryDataForNewRange(newMin, newMax)
  }

  // 查询新范围的数据
  const queryDataForNewRange = async (newMin: number | Date, newMax: number | Date) => {
    try {
      console.log('查询新范围数据:', { newMin, newMax })

      // 准备API参数
      const params = prepareApiParams(newMin, newMax)

      // 调用API获取新范围的数据
      const res = await getApiMethod()(params)

      if (res && res.code === 200) {
        // 更新数据
        let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
        Object.keys(res.data).forEach(key => {
          selectedParamsInfo.value.forEach((param) => {
            if (param.id === key) {
              resdataTemp[param.name] = res.data[key]
            }
          })
        })
        resdataTemp['RecTime'] = res.data['RecTime']
        defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
        timeIntervalList.value = []
        // 获取interval
        if (defaultData.value && defaultData.value.length > 0 ) {
          defaultData.value.forEach((item: any) => {
            timeIntervalList.value.push(item.interval)
          })
        }

        // 保存当前所有参数的y轴范围
        const selectedParams = selectedParametersList.value?.filter(param =>
          param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
        ) || [];

        // 记录每个参数的Y轴范围以便保持不变
        const paramYScaleRanges: Record<string, [number, number]> = {};
        selectedParams.forEach(paramName => {
          if (getParameterDisplayMode(paramName) === 'continuous') {
            const paramYScale = paramYScaleMap.value[paramName];
            if (paramYScale) {
              // 获取当前Y轴范围并保存
              paramYScaleRanges[paramName] = paramYScale.domain() as [number, number];
            }
          }
        });

        // 设置矩形缩放模式标记和保存Y轴范围
        isRectZoomActive.value = true;
        rectZoomYRanges.value = paramYScaleRanges;

        // 设置标记，指示在更新图表时忽略用户设置的Y轴范围
        skipUserYAxisSettings.value = true;

        // 更新图表显示
        updateChartWithParamYScales()
        // 确保游标线和游标点正确显示在相同的屏幕位置
        if (chartContainer.value) {
          const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
          const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
          const newTimestamp = xScale.invert(newCursorX);
          currentCursorPosition.value = newTimestamp;

          // 根据模式更新游标
          if (interactionMode.value === 'measure' && isMeasuring.value) {
            // 使用保存的屏幕位置比例计算新的游标位置
            const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
            const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);

            cursor1Position.value = xScale.invert(cursor1X);
            cursor2Position.value = xScale.invert(cursor2X);

            // 更新测量游标和测量数据
            updateMeasurementCursors();
            updateMeasurementResults();
          } else {
            updateCursorPosition(newCursorX, newTimestamp);
          }

          console.log('缩放后更新游标到屏幕位置比例:', cursorScreenPositionRatio.value);
        }
        // 更新标注高亮
        updateAnnotationHighlights();

        // 记录当前状态到历史记录
        recordZoomState();
      }

    } catch (error) {
      console.error('查询新范围数据失败:', error)
    }
  }
}

// 在onUnmounted中添加缩放带事件清理
onUnmounted(() => {
  window.removeEventListener('mouseup', handleMouseUp)
  window.removeEventListener('keydown', handleKeyDown)
  
  // 清理缩放带事件
  d3.select('body')
    .on('mousemove.zoombar', null)
    .on('mouseup.zoombar', null)
    .on('mouseleave.zoombar', null)
})

// 保存每个参数的Y轴比例尺供矩形缩放使用
const paramYScaleMap = ref<{ [paramName: string]: d3.ScaleLinear<number, number> }>({});

const updateChart = (isZoom: boolean | undefined) => {
  // 此函数负责绘制主区域的曲线图
  // 只有当参数在通道中设置为"连续量"显示方式时，才会在此区域显示曲线
  if (!svg || !chartContainer.value) return

  const width = chartContainer.value.clientWidth
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value // 减去离散量区域和缩放带高度

  // 清除现有线条和Y轴
  svg.selectAll('.line').remove()
  svg.selectAll('.param-y-axis').remove()
  
  // 隐藏初始化的y轴
  svg.select('.initial-y-axis').style('display', 'none')
  
  // 检查并更新裁剪区域
  updateClipPath(width, height)

  // 为每个参数计算独立的Y轴范围
  const paramYScales: { [paramName: string]: d3.ScaleLinear<number, number> } = {}
  const paramDataRanges: { [paramName: string]: { min: number, max: number } } = {}

  // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || [];
  
  // 创建视图设置的副本，用于更新
  const updatedSettings = props.viewSettings ? { ...props.viewSettings } : {};
  
  // 标记是否有更新
  let hasChanges = false;
  
  // 如果是共享Y轴模式，先计算所有参数的总体数据范围
  let globalMinValue = Infinity;
  let globalMaxValue = -Infinity;
  let hasGlobalData = false;

  if (useSharedYAxis.value) {
    // 计算所有参数在所有通道中的全局数据范围
    selectedParams.forEach(paramName => {
      // 只考虑连续模式的参数
      if (getParameterDisplayMode(paramName) !== 'continuous') return;

      defaultData.value.forEach((channel: any) => {
        const channelId = channel.channelId;
        const paramData = channel.parameters[paramName];
        
        if (!paramData || !Array.isArray(paramData)) return;
        
        // 检查此参数在此通道的显示方式
        const displayMode = getChannelSettingDisplayMode(paramName, channelId);
        if (displayMode === 'discrete') return;
        
        // 检查通道可见性
        if (!getChannelSettingVisible(paramName, channelId)) return;
        
        // 获取当前x轴范围内的数据
        const xDomain = xScale.domain();
        const visibleData = paramData.filter(d => {
          // 将时间字符串转换为Date进行比较
          const dataTime = new Date(d.timestamp).getTime()
          const domainStart = (xDomain[0] as Date).getTime()
          const domainEnd = (xDomain[1] as Date).getTime()
          return dataTime >= domainStart && dataTime <= domainEnd
          
        });
        
        if (visibleData.length > 0) {
          // 如果value为NaN，则不参与计算
          const dataMin = Math.min(...visibleData.map(d => d.value === 'NaN' ? Infinity : d.value))
          const dataMax = Math.max(...visibleData.map(d => d.value === 'NaN' ? -Infinity : d.value))
          globalMinValue = Math.min(globalMinValue, dataMin);
          globalMaxValue = Math.max(globalMaxValue, dataMax);
          hasGlobalData = true;
        }
      });
    });

    // 计算全局Y轴范围
    if (hasGlobalData && globalMinValue !== Infinity && globalMaxValue !== -Infinity) {
      const valueRange = globalMaxValue - globalMinValue;
      
      // 扩展范围，使曲线显示在图表中间
      const paddingFactor = 0.4; // 上下各预留1/5，总扩展是0.4
      const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
      
      // 全局自动Y轴范围
      const autoYMin = globalMinValue - padding / 2;
      const autoYMax = globalMaxValue + padding / 2;
      
      // 使用全局范围更新Y轴
      yScale.domain([autoYMin, autoYMax]);
    }
  }
  
  selectedParams.forEach(paramName => {
    let minValue = Infinity
    let maxValue = -Infinity
    let hasData = false

    // 如果是共享Y轴模式，直接使用全局Y轴比例尺
    if (useSharedYAxis.value && hasGlobalData) {
      paramYScales[paramName] = yScale;
      paramYScaleMap.value[paramName] = yScale;
      return; // 跳过为单个参数计算范围
    }

    // 遍历所有通道计算每个参数的数据范围
    defaultData.value.forEach(channel => {
      const channelId = channel.channelId
      const paramData = channel.parameters[paramName]
      
      if (!paramData || !Array.isArray(paramData)) return
      
      // 检查此参数在此通道的显示方式
      const displayMode = getChannelSettingDisplayMode(paramName, channelId)
      // 跳过设置为离散显示的参数/通道
      if (displayMode === 'discrete') return
      
      // 检查通道可见性
      if (!getChannelSettingVisible(paramName, channelId)) return
      
      // 获取当前x轴范围内的数据
      const xDomain = xScale.domain()
      const visibleData = paramData.filter(d => {
        // RJ模式：将时间字符串转换为Date对象
        const dataTime = new Date(d.timestamp as string).getTime()
        const domainStart = (xDomain[0] as Date).getTime()
        const domainEnd = (xDomain[1] as Date).getTime()
        return dataTime >= domainStart && dataTime <= domainEnd
      })

      if (visibleData.length > 0) {
        // 如果value为NaN，则不参与计算
        const dataMin = Math.min(...visibleData.map(d => d.value === 'NaN' ? Infinity : d.value))
        const dataMax = Math.max(...visibleData.map(d => d.value === 'NaN' ? -Infinity : d.value))
        minValue = Math.min(minValue, dataMin)
        maxValue = Math.max(maxValue, dataMax)
        hasData = true
      }
    })
    // 如果找到了有效的最大值和最小值
    if (hasData && minValue !== Infinity && maxValue !== -Infinity) {
      const valueRange = maxValue - minValue
      
      // 扩展范围，使曲线显示在图表中间的3/5范围
      const paddingFactor = 0.4 // 上下各预留1/5，所以总扩展是0.4
      const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
      
      // 计算自动的Y轴范围
      const autoYMin = minValue - padding / 2;
      const autoYMax = maxValue + padding / 2;
      
      // 检查是否有用户设置的Y轴范围
      const userYAxisMin = getParameterYAxisMin(paramName);
      const userYAxisMax = getParameterYAxisMax(paramName);
      
      // 最终使用的Y轴范围
      let yMin, yMax;
      
      // 如果需要忽略用户设置（在缩放操作后），直接使用自动计算的范围
      if (skipUserYAxisSettings.value) {
        yMin = autoYMin;
        yMax = autoYMax;
      } else {
        // 否则尊重用户设置的范围
        // 如果用户设置了下限，则使用用户设置
        if (userYAxisMin !== undefined) {
          yMin = userYAxisMin;
        } else {
          yMin = autoYMin;
        }
        
        // 如果用户设置了上限，则使用用户设置
        if (userYAxisMax !== undefined) {
          yMax = userYAxisMax;
        } else {
          yMax = autoYMax;
        }
      }
      
      // 确保yMin < yMax，防止用户设置错误
      if (yMin >= yMax) {
        console.warn(`参数 ${paramName} 的Y轴范围设置有误 (${yMin} >= ${yMax})，已使用自动计算范围`);
        yMin = autoYMin;
        yMax = autoYMax;
      }
      
      // 保存参数范围用于后续绘制
      paramDataRanges[paramName] = { min: yMin, max: yMax }
      
      // 创建每个参数的独立Y刻度并保存到全局映射中
      paramYScales[paramName] = d3.scaleLinear()
        .domain([yMin, yMax])
        .range([height - margin.bottom, margin.top])
      
      // 保存到全局比例尺映射中供矩形缩放使用
      paramYScaleMap.value[paramName] = paramYScales[paramName];
      
      // 更新全局Y轴范围（用于非矩形缩放模式）
      if (paramName === selectedParams[selectedParams.length - 1]) {
        yScale.domain([yMin, yMax]);
      }
    }
  })
  
  // 如果视图设置有变化，发送更新事件
  if (hasChanges) {
    emit('update:viewSettings', updatedSettings);
  }
  
  // 清除现有的Y轴和背景
  svg.selectAll('.param-y-axis').remove();
  svg.selectAll('.y-axis-background').remove();
  
  // 找到最后一个连续量参数
  let lastContinuousParam: string | null = null;
  for (let i = selectedParams.length - 1; i >= 0; i--) {
    const paramName = selectedParams[i];
    if (getParameterDisplayMode(paramName) === 'continuous' && paramYScales[paramName]) {
      lastContinuousParam = paramName;
      break;
    }
  }

  // 共享Y轴模式下只添加一个Y轴
  if (useSharedYAxis.value && hasGlobalData) {
    const yAxis = d3.axisLeft(yScale).ticks(5).tickFormat(formatAxisNumber);
    const yAxisGroup = svg.append('g')
      .attr('class', 'param-y-axis global-y-axis')
      .attr('transform', `translate(${margin.left},0)`)
      .style('opacity', 1) // 设置为可见
      .call(yAxis);
  } else {
    // 独立Y轴模式下，为每个参数创建Y轴，但都默认设置为不可见
    selectedParams.forEach(paramName => {
      const paramScale = paramYScales[paramName];
      
      if (paramScale) {        
        // 添加Y轴
        const yAxis = d3.axisLeft(paramScale).ticks(5).tickFormat(formatAxisNumber);
        const escapedParamName = escapeSelector(paramName);
        const yAxisGroup = svg.append('g')
          .attr('class', `param-y-axis param-${escapedParamName}`)
          .attr('transform', `translate(${margin.left},0)`)
          .style('opacity', 0) // 默认设置为不可见
          .style('font-size', '12px') // 设置Y轴刻度文本的字体大小
          .call(yAxis);
        
        // 添加参数名称
        yAxisGroup.append('text')
          .attr('class', 'axis-title')
          .attr('transform', 'rotate(0)') // 取消旋转
          .attr('y', 15) // 将文本上移到坐标轴上方
          .attr('x', -40) // 稍微右移一点
          .attr('dy', '0') // 取消垂直偏移
          .attr('fill', isDarkMode.value ? '#cccccc' : '#333')
          .attr('font-size', '11px')
          .style('text-anchor', 'start') // 左对齐
          .text(getShowName(paramName));
      }
    });

    // 只显示最后一个连续量参数的Y轴
    if (lastContinuousParam) {
      svg.select(`.param-${escapeSelector(lastContinuousParam)}`)
        .style('opacity', 1);
    }
  }

  // 创建一个包含所有线条的组，并应用裁剪
  const linesGroup = svg.append('g')
    .attr('class', 'lines-group')
    .attr('clip-path', 'url(#chart-area)')

  // 为每个通道的每个参数绘制曲线
  defaultData.value.forEach((channel: any) => {
    const channelId = channel.channelId
    
    // 遍历选中的参数
    selectedParams.forEach((paramName: string) => {
      const paramData = channel.parameters[paramName]
      
      if (!paramData || !Array.isArray(paramData)) {
        console.log(`No data for parameter ${paramName} in channel ${channelId}`)
        return
      }
      
      // 检查视图设置中的可见性
      if (!getChannelSettingVisible(paramName, channelId)) {
        return // 跳过不可见的参数/通道
      }
      
      // 检查显示方式，如果设置为离散量则不绘制曲线
      const displayMode = getChannelSettingDisplayMode(paramName, channelId)
      if (displayMode === 'discrete') {
        // 设置为离散量显示的参数将在离散区域以色带形式显示，这里不绘制曲线
        return
      }
      
      // 获取该参数的Y轴比例尺
      const yScale = paramYScales[paramName]
      if (!yScale) return // 如果没有对应的Y轴则跳过
      
      // 在RJ模式下扩展数据，将最后一个数据点延续到x轴右边界之后
      // 首先将所有数据转换为普通对象，避免响应式Proxy与普通对象混合
      let extendedData = paramData.map(point => ({
        timestamp: point.timestamp,
        value: point.value
      }));
      
      if (extendedData.length > 0) {
        const lastPoint = extendedData[extendedData.length - 1];
        const xDomain = xScale.domain();
        const maxTime = xDomain[1] as Date;
        const lastDataTime = new Date(lastPoint.timestamp as string);
        
        // 始终添加一个延续点，延续到最后数据点的下一秒，确保覆盖完整的最后一秒
        const extendTime = new Date(Math.max(maxTime.getTime(), lastDataTime.getTime() + 1000));
        // 加上8小时的时区偏移（中国时区UTC+8）
        const extendTimeWithOffset = new Date(extendTime.getTime() + 8 * 60 * 60 * 1000);
        // 创建延续点，保持数据结构一致性
        const extendedPoint = {
          timestamp: extendTimeWithOffset.toISOString().replace('T', ' ').replace('Z', '').substring(0, 19),
          value: lastPoint.value
        };
        extendedData.push(extendedPoint);
      }
      
      const line = d3.line<{ timestamp: number | string; value: number }>()
        .x(d => {
          
            // RJ模式：将时间字符串转换为Date对象
            return xScale(new Date(d.timestamp as string))
         
        })
        .y(d => yScale(d.value))
        .curve(getLineType())
        .defined(d => {
          // 过滤掉值为NaN或无效的数据点
            // RJ模式：检查时间戳和值是否有效
            const timestamp = new Date(d.timestamp as string);
            return !isNaN(timestamp.getTime()) && !isNaN(d.value) && isFinite(d.value);
         
        })

      // 获取线条粗细
      const lineWidth = getChannelSettingLineWidth(paramName, channelId)

      linesGroup.append('path')
        .datum(extendedData)
        .attr('class', `line line-${escapeSelector(paramName)}-${channelId}`)
        .attr('fill', 'none')
        .attr('stroke', getChannelColor(channelId, paramName))
        .attr('stroke-width', lineWidth)
        .attr('d', line)
        .style('pointer-events', 'none'); // 添加这一行，禁用曲线对鼠标事件的响应，允许事件传递到底层
    })
  })
  
  // 更新离散量区域
  updateDiscreteArea()
  
  // 更新缩放带和离散区域位置
  updateZoomBarPosition()
  
  // 确保缩放窗口位置与当前X轴范围同步
  updateZoomWindowPosition()
  // 缩放操作不重新渲染概览图
  if (svg && chartContainer.value && !isZoom) {
    const zoomBarGroup = svg.select('.zoom-bar-group') as d3.Selection<SVGGElement, unknown, null, undefined>;
    if (!zoomBarGroup.empty()) {
      const width = chartContainer.value.clientWidth;
      const height = chartContainer.value.clientHeight;
      // 重新绘制缩放带概览图
      updateOverviewChart(zoomBarGroup, width, height);
    }
  }

  // 如果游标已存在，更新游标点
  if (svg && xScale) {
    if (interactionMode.value !== 'measure') {
      // 获取当前游标位置
      const cursorLine = svg.select('.cursor-line')
      if (!cursorLine.empty()) {
        const x = parseFloat(cursorLine.attr('x1'))
        const timestamp = xScale.invert(x)
        
        // 更新游标点显示
          // RJ模式：timestamp是Date类型
          updateCursorPosition(x, (timestamp as Date).getTime());
          updateCursorPoints((timestamp as Date).getTime())
          
          // 重新发送游标位置，触发参数值更新
          emit('update:cursorPosition', (timestamp as Date).getTime())
        
      }
    } else if (interactionMode.value === 'measure' && isMeasuring.value) {    
      // 更新测量游标显示和数据
      updateMeasurementCursors();
      updateMeasurementResults();
    }
  }
  
  // 确保应用当前主题颜色
  applyThemeColors()
  
  // 更新概览图以反映最新的颜色设置
  if (svg && chartContainer.value) {
    const zoomBarGroup = svg.select('.zoom-bar-group') as d3.Selection<SVGGElement, unknown, null, undefined>;
    if (!zoomBarGroup.empty()) {
      const width = chartContainer.value.clientWidth;
      const height = chartContainer.value.clientHeight;
      // 重新绘制缩放带概览图以更新颜色
      updateOverviewChart(zoomBarGroup, width, height);
    }
  }
  
  // 如果故障模式激活且有选中的故障，重新绘制故障线
  if (isFaultModeActive.value && currentSelectedFaultIds.value && currentSelectedFaultIds.value.length > 0 && currentFaultList.value && !updateFaultArea.value) {
    updateFaultDisplayMode('', currentSelectedFaultIds.value, currentFaultList.value)
    updateFaultArea.value = false
  } else {
    updateFaultArea.value = false
  }

  // 重新绘制后更新标注高亮，确保高亮能正确显示
  if (annotationHighlights.value.length > 0) {
    updateAnnotationHighlights()
  }
}

// 获取通道颜色
function getChannelColor(channelId: string, paramName?: string): string {
  if (paramName) {
    // 获取参数基础颜色
    const baseColor = getParameterBaseColor(paramName);
    
    // 获取该参数所有有数据的通道
    const channelsWithData = getChannelsWithData(paramName);
    const channelIndex = channelsWithData.indexOf(channelId);
    const totalChannels = channelsWithData.length;
    
    // 如果有多个通道，生成变体色
    if (totalChannels > 1 && channelIndex >= 0) {
      // 为通道生成颜色变体
      return generateChannelColorVariant(baseColor, channelIndex, totalChannels);
    }
    
    // 只有一个通道或找不到通道索引时直接返回基础颜色
    return baseColor;
  }
  
  // 没有提供参数名时返回默认颜色
  return '#1890ff';
}

// 生成通道颜色变体的函数
const generateChannelColorVariant = (baseColor: string, channelIndex: number, totalChannels: number): string => {
  // 如果是不带#的颜色格式，添加#
  if (!baseColor.startsWith('#')) {
    baseColor = '#' + baseColor;
  }
  
  // 将十六进制颜色转换为HSL
  const hex = baseColor.substring(1);
  const r = parseInt(hex.substring(0, 2), 16) / 255;
  const g = parseInt(hex.substring(2, 4), 16) / 255;
  const b = parseInt(hex.substring(4, 6), 16) / 255;
  
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  const l = (max + min) / 2;
  
  let h = 0;
  let s = 0;
  
  if (max !== min) {
    const d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    
    if (max === r) {
      h = (g - b) / d + (g < b ? 6 : 0);
    } else if (max === g) {
      h = (b - r) / d + 2;
    } else {
      h = (r - g) / d + 4;
    }
    
    h *= 60;
  }
  
  // 调整HSL值来生成变体 - 保持同一色系但区别更明显
  // 为每个通道创建明显不同的颜色变体
  let adjustedH = h;
  let adjustedS = s;
  let adjustedL = l;
  
  if (channelIndex === 0) {
    // 第一个通道保持基础颜色
    adjustedH = h;
    adjustedS = s;
    adjustedL = l;
  } else {
    // 在同一色系内调整 - 不改变色相，但大幅调整饱和度和亮度
    
    // 调整饱和度 - 增加更多饱和度使颜色更鲜艳
    adjustedS = Math.min(1, s + 0.1 * channelIndex);
    
    // 通道2和4变暗，通道3变亮 - 形成非常明显的对比
    if (channelIndex === 2) { // 第二个通道
      adjustedL = Math.min(0.7, l + 0.15);
    } else if (channelIndex === 1) { // 第三个通道
      adjustedL = Math.min(0.85, l + 0.3);
    } else { // 第四个通道
      adjustedL = Math.max(0.4, l - 0.2);
    }
  }
  
  return hslToHex(adjustedH, adjustedS * 100, adjustedL * 100);
}

// 开始测量
const startMeasurement = async () => {
  if (!chartContainer.value || !svg) return;
  
  // 显示测量相关UI元素
  isMeasuring.value = true;
  
  // 设置初始光标位置在X轴范围的1/3和2/3处
  const xDomain = xScale.domain();
  
  // RJ模式：处理Date类型
  const startTime = (xDomain[0] as Date).getTime();
  const endTime = (xDomain[1] as Date).getTime();
  const timeRange = endTime - startTime;
  cursor1Position.value = new Date(startTime + (timeRange / 3));
  cursor2Position.value = new Date(startTime + (timeRange * 2 / 3));
  
  
  // 初始化测量游标的屏幕位置比例
  cursor1ScreenRatio.value = 1/3; // 屏幕宽度的1/3处
  cursor2ScreenRatio.value = 2/3; // 屏幕宽度的2/3处
  
  // 激活第一个游标
  activeCursor.value = 'cursor1';
  
  // 隐藏普通游标
  svg.select('.cursor-line').style('display', 'none');
  svg.selectAll('.cursor-group').style('display', 'none');
  svg.selectAll('.cursor-points-group').style('display', 'none');
  
  // 清除普通模式下的游标线
  svg.selectAll('.discrete-cursor-line').remove();
  
  // 清除可滚动容器中的游标线
  const discreteScrollContainer = document.querySelector('.discrete-scroll-container');
  if (discreteScrollContainer) {
    const innerSvg = discreteScrollContainer.querySelector('.discrete-inner-svg');
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
    }
  }
  
  // 初始化测量游标
  updateMeasurementCursors();

  // 获取游标位置，转换为数值类型用于比较
  let cursor1: number;
  let cursor2: number;
  
  // RJ模式：安全地处理各种时间格式
  if (cursor1Position.value && typeof cursor1Position.value === 'object' && (cursor1Position.value as any).getTime) {
    cursor1 = (cursor1Position.value as Date).getTime();
  } else if (typeof cursor1Position.value === 'number') {
    cursor1 = cursor1Position.value < 10000000000 ? cursor1Position.value * 1000 : cursor1Position.value;
  } else if (typeof cursor1Position.value === 'string') {
    cursor1 = new Date(cursor1Position.value).getTime();
  } else {
    cursor1 = Date.now();
  }
  
  if (cursor2Position.value && typeof cursor2Position.value === 'object' && (cursor2Position.value as any).getTime) {
    cursor2 = (cursor2Position.value as Date).getTime();
  } else if (typeof cursor2Position.value === 'number') {
    cursor2 = cursor2Position.value < 10000000000 ? cursor2Position.value * 1000 : cursor2Position.value;
  } else if (typeof cursor2Position.value === 'string') {
    cursor2 = new Date(cursor2Position.value).getTime();
  } else {
    cursor2 = Date.now();
  }
  let deltaTime = 0
  deltaTime = Number((Math.abs(cursor1 - cursor2) / 1000).toFixed(0))
  // 创建结果对象
  const measurementResults: Record<string, {
    parameterId: string,
    channelId: string,
    min: number,
    max: number,
    avg: number,
    fluctuation: number
  }> = {}
  let measurementResultsTemp: Record<string, {
    parameterId: string,
    channelId: string,
    min: number,
    max: number,
    avg: number,
    fluctuation: number
  }> = {}
  // 设置测量数据loading状态
  isMeasurementLoading.value = true
  emit('update:measurementLoading', true)
  // TODO从后端获取测量数据
  try {
    const pad = (n: number) => n < 10 ? '0' + n : n;
    const formatDate = (date: number) => {
      const d = new Date(date);
      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    };
    let cursor1Date = '';
    let cursor2Date = '';
    if (cursor1 < cursor2) {
      cursor1Date = formatDate(cursor1);
      cursor2Date = formatDate(cursor2);
    } else {
      cursor1Date = formatDate(cursor2);
      cursor2Date = formatDate(cursor1);
    }
    let columnList = selectedParamsInfo.value
    if (columnList.length == 0) {
      measurementResultsTemp = {};
    } else {
      // 将 columnList 转换为 { id: name, id: name } 的对象格式
      const columnListObj = columnList.reduce((acc, item) => {
        acc[item.id] = item.name;
        return acc;
      }, {} as { [key: string]: string })
      let params = {
        unitCode: unitCode.value,
        startTime: cursor1Date,
        endTime: cursor2Date,
        params: columnListObj,
      };
      const res = await API.queryMeasurementData(params);
      if (res && res.code === 200) {
        measurementResultsTemp = res.result || res.data || {};
      } else {
        measurementResultsTemp = {};
        console.warn("查询测量数据返回异常，code:", res?.code);
        messageRef.value?.show({
          type: "warning",
          content: res?.message || "测量数据获取失败",
          duration: 3000,
        });
      }
    }
  } catch (error: any) {
    measurementResultsTemp = {};
    console.error("Failed to fetch measurement data:", error);
    messageRef.value?.show({
      type: "error",
      content: error.response?.data?.msg || error.message || "测量数据获取失败",
      duration: 3000,
    });
  } finally {
    // 无论成功或失败，都关闭loading状态
    isMeasurementLoading.value = false
    emit('update:measurementLoading', false)
  }
  Object.keys(measurementResultsTemp).forEach(key => {
    selectedParamsInfo.value.forEach(param => {
      if (param.id === key) {
        measurementResults[param.name] = measurementResultsTemp[key]
      }
    })
  })
  Object.keys(measurementResults).forEach(key => {
    measurementResults[key].parameterId = key
    measurementResults[key].channelId = 'RJ'
  })
  emit('update:measurementData', {
    cursor1,
    cursor2,
    deltaTime,
    measurementResults,
  })
}

// 更新测量游标函数，添加游标手柄以便于拖动
const updateMeasurementCursors = () => {
  if (!svg || !chartContainer.value) return;
  
  console.log('更新测量游标', cursor1Position.value, cursor2Position.value);
  
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value;
  
  // 确保普通游标被隐藏
  svg.select('.cursor-line').style('display', 'none');
  svg.selectAll('.cursor-group').style('display', 'none');
  svg.selectAll('.cursor-points-group').style('display', 'none');
  
  // 移除所有类型的游标线，确保干净的环境
  svg.selectAll('.cursor-line-1,.cursor-line-2').remove();
  svg.selectAll('.measurement-cursor-group').remove();
  svg.selectAll('.discrete-cursor-line').remove();
  svg.selectAll('.fault-cursor-line').remove();
  
  // 清除可滚动容器中的游标线
  const discreteScrollContainer = document.querySelector('.discrete-scroll-container');
  if (discreteScrollContainer) {
    const innerSvg = discreteScrollContainer.querySelector('.discrete-inner-svg');
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
    }
  }
  // 清除故障带滚动区域中的游标线
  const faultBandScrollContainer = document.querySelector('.fault-scroll-container');
  if (faultBandScrollContainer) {
    const innerSvg = faultBandScrollContainer.querySelector('.fault-inner-svg');
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.fault-cursor-line').remove();
    }
  }
  // 计算游标的屏幕坐标
  const x1 = xScale(cursor1Position.value as any);
  const x2 = xScale(cursor2Position.value as any);
  
  // 创建测量游标组并应用裁剪
  const cursorGroup = svg.append('g')
    .attr('class', 'measurement-cursor-group')
    .attr('clip-path', 'url(#chart-area)');
  
  // 添加实线游标（激活游标）
  cursorGroup.append('line')
    .attr('class', 'cursor-line-1')
    .attr('x1', activeCursor.value === 'cursor1' ? x1 : x2)
    .attr('x2', activeCursor.value === 'cursor1' ? x1 : x2)
    .attr('y1', margin.top)
    .attr('y2', chartContainer.value!.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) // 只延伸到主图表区域
    .attr('stroke', '#1890ff') // 使用主题蓝色
    .attr('stroke-width', 2)
    .style('display', 'block')
    .style('cursor', 'pointer')
    .on('mousedown', (event) => {
      // 点击实线游标开始拖动
      event.stopPropagation(); // 防止事件冒泡触发底层的mousedown
      activeCursor.value = activeCursor.value === 'cursor1' ? 'cursor2' : 'cursor1'
      isDragging.value = true; // 设置为拖动状态
      updateMeasurementCursors();
    })
  
  // 创建一个渐变用于激活的手柄
  const gradientId = 'activeHandleGradient';
  let gradient = svg.select(`#${gradientId}`);
  
  if (gradient.empty()) {
    gradient = svg.append('defs')
      .append('linearGradient')
      .attr('id', gradientId)
      .attr('x1', '0%')
      .attr('y1', '0%')
      .attr('x2', '0%')
      .attr('y2', '100%');
      
    gradient.append('stop')
      .attr('offset', '0%')
      .attr('stop-color', '#2563eb')
      .attr('stop-opacity', 1);
      
    gradient.append('stop')
      .attr('offset', '100%')
      .attr('stop-color', '#1e40af')
      .attr('stop-opacity', 1);
  }
  
  // 创建一个渐变用于非激活的手柄
  const inactiveGradientId = 'inactiveHandleGradient';
  let inactiveGradient = svg.select(`#${inactiveGradientId}`);
  
  if (inactiveGradient.empty()) {
    inactiveGradient = svg.append('defs')
      .append('linearGradient')
      .attr('id', inactiveGradientId)
      .attr('x1', '0%')
      .attr('y1', '0%')
      .attr('x2', '0%')
      .attr('y2', '100%');
      
    inactiveGradient.append('stop')
      .attr('offset', '0%')
      .attr('stop-color', '#60a5fa')
      .attr('stop-opacity', 1);
      
    inactiveGradient.append('stop')
      .attr('offset', '100%')
      .attr('stop-color', '#3b82f6')
      .attr('stop-opacity', 1);
  }
  
  // 添加激活游标的手柄
  const activeX = activeCursor.value === 'cursor1' ? x1 : x2;
  
  // 添加激活游标手柄的背景阴影
  cursorGroup.append('rect')
    .attr('class', 'cursor-handle-active-shadow')
    .attr('x', activeX - 9)
    .attr('y', margin.top + 7)
    .attr('width', 18)
    .attr('height', 27)
    .attr('fill', 'rgba(0, 0, 0, 0.2)')
    .attr('rx', 6)
    .attr('ry', 6);
  
  // 添加激活游标的手柄
  cursorGroup.append('rect')
    .attr('class', 'cursor-handle-active')
    .attr('x', activeX - 8)
    .attr('y', margin.top + 5)
    .attr('width', 16)
    .attr('height', 26)
    .attr('fill', `url(#${gradientId})`)
    .attr('stroke', '#1e40af')
    .attr('stroke-width', 1)
    .attr('rx', 5)
    .attr('ry', 5)
    .style('cursor', 'move')
    .style('display', 'block')
    .on('mouseover', function() {
      d3.select(this)
        .attr('stroke-width', 2)
        .attr('width', 18)
        .attr('x', activeX - 9)
        .attr('height', 28)
      
      d3.select('.cursor-handle-active-shadow')
        .attr('width', 20)
        .attr('height', 30)
        .attr('x', activeX - 10)
    })
    .on('mouseout', function() {
      d3.select(this)
        .attr('stroke-width', 1)
        .attr('width', 16)
        .attr('x', activeX - 8)
        .attr('height', 26)
      
      d3.select('.cursor-handle-active-shadow')
        .attr('width', 18)
        .attr('height', 27)
        .attr('x', activeX - 9)
    })
    .on('mousedown', function(event) {
      // 点击手柄开始拖动
      event.stopPropagation();
      isDragging.value = true;
    });
  
  // 在激活手柄中添加三条线条作为指示图案
  for (let i = 0; i < 3; i++) {
    cursorGroup.append('line')
      .attr('x1', activeX - 4)
      .attr('y1', margin.top + 11 + i * 5)
      .attr('x2', activeX + 4)
      .attr('y2', margin.top + 11 + i * 5)
      .attr('stroke', 'white')
      .attr('stroke-width', 1.5)
      .attr('stroke-linecap', 'round');
  }
  
  // 添加虚线游标（非激活游标）
  cursorGroup.append('line')
    .attr('class', 'cursor-line-2')
    .attr('x1', activeCursor.value === 'cursor2' ? x1 : x2)
    .attr('x2', activeCursor.value === 'cursor2' ? x1 : x2)
    .attr('y1', margin.top)
    .attr('y2', chartContainer.value!.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) // 只延伸到主图表区域
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1.5)
    .attr('stroke-dasharray', '5,5')
    .style('display', 'block')
    .style('cursor', 'pointer')
    .on('mousedown', (event) => {
      // 点击虚线游标开始拖动
      event.stopPropagation(); // 防止事件冒泡触发底层的mousedown
      activeCursor.value = activeCursor.value === 'cursor1' ? 'cursor2' : 'cursor1'
      isDragging.value = true; // 设置为拖动状态
      updateMeasurementCursors();
    })
  
  // 添加非激活游标的手柄背景阴影
  const inactiveX = activeCursor.value === 'cursor2' ? x1 : x2;
  cursorGroup.append('rect')
    .attr('class', 'cursor-handle-inactive-shadow')
    .attr('x', inactiveX - 9)
    .attr('y', margin.top + 7)
    .attr('width', 18)
    .attr('height', 27)
    .attr('fill', 'rgba(0, 0, 0, 0.1)')
    .attr('rx', 6)
    .attr('ry', 6);
  
  // 添加非激活游标的手柄
  cursorGroup.append('rect')
    .attr('class', 'cursor-handle-inactive')
    .attr('x', inactiveX - 8)
    .attr('y', margin.top + 5)
    .attr('width', 16)
    .attr('height', 26)
    .attr('fill', `url(#${inactiveGradientId})`)
    .attr('stroke', '#3b82f6')
    .attr('stroke-width', 1)
    .attr('rx', 5)
    .attr('ry', 5)
    .attr('stroke-dasharray', '0')
    .style('cursor', 'move')
    .style('display', 'block')
    .on('mouseover', function() {
      d3.select(this)
        .attr('stroke-width', 2)
        .attr('width', 18)
        .attr('x', inactiveX - 9)
        .attr('height', 28)
      
      d3.select('.cursor-handle-inactive-shadow')
        .attr('width', 20)
        .attr('height', 30)
        .attr('x', inactiveX - 10)
    })
    .on('mouseout', function() {
      d3.select(this)
        .attr('stroke-width', 1)
        .attr('width', 16)
        .attr('x', inactiveX - 8)
        .attr('height', 26)
      
      d3.select('.cursor-handle-inactive-shadow')
        .attr('width', 18)
        .attr('height', 27)
        .attr('x', inactiveX - 9)
    })
    .on('mousedown', function(event) {
      // 点击手柄开始拖动，并切换激活的游标
      event.stopPropagation();
      activeCursor.value = activeCursor.value === 'cursor1' ? 'cursor2' : 'cursor1';
      isDragging.value = true;
      updateMeasurementCursors();
    });
  
  // 在非激活手柄中添加三条线条作为指示图案
  for (let i = 0; i < 3; i++) {
    cursorGroup.append('line')
      .attr('x1', inactiveX - 4)
      .attr('y1', margin.top + 11 + i * 5)
      .attr('x2', inactiveX + 4)
      .attr('y2', margin.top + 11 + i * 5)
      .attr('stroke', 'white')
      .attr('stroke-width', 1)
      .attr('stroke-linecap', 'round')
      .attr('stroke-dasharray', '0');
  }
  
  // 添加游标到离散量区域
  const discreteGroup = svg.select('.discrete-group')
  if (!discreteGroup.empty()) {
    // 添加离散区域活动游标线
    discreteGroup.append('line')
      .attr('class', 'discrete-cursor-line')
      .attr('x1', activeCursor.value === 'cursor1' ? x1 : x2)
      .attr('x2', activeCursor.value === 'cursor1' ? x1 : x2)
      .attr('y1', 0 + faultBandsAreaHeight.value)
      .attr('y2', discreteAreaHeight.value + faultBandsAreaHeight.value)
      .attr('stroke', '#1890ff') // 使用主题蓝色
      .attr('stroke-width', 2)
    
    // 添加离散区域非活动游标线
    discreteGroup.append('line')
      .attr('class', 'discrete-cursor-line')
      .attr('x1', activeCursor.value === 'cursor2' ? x1 : x2)
      .attr('x2', activeCursor.value === 'cursor2' ? x1 : x2)
      .attr('y1', 0 + faultBandsAreaHeight.value)
      .attr('y2', discreteAreaHeight.value + faultBandsAreaHeight.value)
      .attr('stroke', '#1890ff')
      .attr('stroke-width', 1.5)
      .attr('stroke-dasharray', '5,5')
  }
  
  // 为离散区域的可滚动容器添加测量游标线
  if (discreteScrollContainer) {
    const innerSvg = discreteScrollContainer.querySelector('.discrete-inner-svg');
    if (innerSvg) {
      const innerSvgSelection = d3.select(innerSvg);
      const innerHeight = innerSvg.getAttribute('height');
      
      // 添加活动游标线
      innerSvgSelection.append('line')
        .attr('class', 'discrete-cursor-line')
        .attr('x1', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('y1', 0)
        .attr('y2', innerHeight)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 2);
      
      // 添加非活动游标线
      innerSvgSelection.append('line')
        .attr('class', 'discrete-cursor-line')
        .attr('x1', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('y1', 0)
        .attr('y2', innerHeight)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 1.5)
        .attr('stroke-dasharray', '5,5');
    }
  }

  // 添加测量游标到故障区域
  const faultScrollContainer = document.querySelector('.fault-scroll-container');
  if (faultScrollContainer) {
    const innerSvg = faultScrollContainer.querySelector('.fault-inner-svg');
    if (innerSvg) {
      // 先移除内部SVG中可能存在的旧游标线
      d3.select(innerSvg).selectAll('.fault-cursor-line').remove();
      
      const innerSvgSelection = d3.select(innerSvg);
      const innerHeight = parseFloat(innerSvg.getAttribute('height') || '0');
      
      // 添加活动游标线
      innerSvgSelection.append('line')
        .attr('class', 'fault-cursor-line')
        .attr('x1', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('y1', 0)
        .attr('y2', innerHeight)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 2)
        .style('pointer-events', 'none');
      
      // 添加非活动游标线
      innerSvgSelection.append('line')
        .attr('class', 'fault-cursor-line')
        .attr('x1', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('y1', 0)
        .attr('y2', innerHeight)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 1.5)
        .attr('stroke-dasharray', '5,5')
        .style('pointer-events', 'none');
    }
  } else {
    // 使用常规方式添加游标线到主SVG的故障带区域
    const faultBandGroup = svg.select('.fault-band');
    if (!faultBandGroup.empty()) {
      // 计算故障带区域的位置
      const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value;
      const faultBandStartY = height;
      
      // 添加活动游标线
      svg.append('line')
        .attr('class', 'fault-cursor-line')
        .attr('x1', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor1' ? x1 : x2)
        .attr('y1', faultBandStartY)
        .attr('y2', faultBandStartY + faultBandsAreaHeight.value - 10)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 2)
        .style('pointer-events', 'none');
      
      // 添加非活动游标线
      svg.append('line')
        .attr('class', 'fault-cursor-line')
        .attr('x1', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('x2', activeCursor.value === 'cursor2' ? x1 : x2)
        .attr('y1', faultBandStartY)
        .attr('y2', faultBandStartY + faultBandsAreaHeight.value - 10)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 1.5)
        .attr('stroke-dasharray', '5,5')
        .style('pointer-events', 'none');
    }
  }
  
  // 获取激活游标的位置
  const activePosition = activeCursor.value === 'cursor1' ? cursor1Position.value : cursor2Position.value
  
  // 发送当前激活游标的位置
  emit('update:cursorPosition', activePosition)
  
  // 获取激活游标位置的数据并发送
  const cursorData = getCursorData(activePosition)
  emit('update:cursorData', cursorData)
  
  // 更新游标标签
  updateCursorLabels()
}

// 计算并发送测量数据
const calculateMeasurementData = () => {
  // 不从前端计算
}

// 重置多边形数据
const resetPolygon = () => {
  // 清空多边形点集
  polygonPoints.value = []
  
  // 移除绘制中的多边形
  if (currentPolygon.value) {
    currentPolygon.value.remove()
    currentPolygon.value = null
  }
  
  // 移除多边形点
  svg.selectAll('.polygon-point').remove()
  
  // 移除临时连接线
  if (tempLine.value) {
    tempLine.value.remove()
    tempLine.value = null
  }
  
  // 隐藏多边形绘制区域
  showPolygon.value = false
  
  // 隐藏数据抽取结果
  showPolygonData.value = false
}

// 全景预览
const showPreview = () => {
  isPreview.value = !isPreview.value
  //点击全景预览后需要重新查询数据并绘图
  // 准备API参数，获取完整数据
  const params = prepareApiParams(undefined, undefined, true)
  
  // 查询完整数据
  queryChartData(params).then(data => {
    if (data && data.length > 0) {
      // 更新初始域并重置x轴范围
      // RJ模式：处理时间字符串
      let minTime: Date | null = null;
      let maxTime: Date | null = null;
      
      data.forEach((channel: any) => {
        Object.entries(channel.parameters).forEach(([paramId, paramData]: [string, any]) => {
          if (!paramData || !Array.isArray(paramData)) return
          
          if (paramData.length > 0) {
            paramData.forEach((d: any) => {
              const timestamp = new Date(d.timestamp);
              if (!isNaN(timestamp.getTime())) {
                if (!minTime || timestamp < minTime) {
                  minTime = timestamp;
                }
                if (!maxTime || timestamp > maxTime) {
                  maxTime = timestamp;
                }
              }
            });
          }
        })
      })
      
      if (minTime && maxTime) {
        // 将时间精确到秒级，去除毫秒
        const roundedMinTime = new Date(Math.floor(minTime.getTime() / 1000) * 1000);
        const roundedMaxTime = new Date(Math.floor(maxTime.getTime() / 1000) * 1000);
        initialDomain.value = [roundedMinTime, roundedMaxTime];
        xScale.domain([roundedMinTime, roundedMaxTime]);
      }
      updateAxes();
      
      // 重新绘制图表
      // updateChartWithParamYScales();
      updateChart(false);
      
      // 重置缩放窗口位置
      resetZoomWindowPosition();
      
      // 清空缩放历史
      zoomHistory.value = [];
      
      // 重置矩形缩放状态
      isRectZoomActive.value = false;
      rectZoomYRanges.value = {};
    }
  }).catch(error => {
    console.error('全景预览数据查询失败:', error);
  });
  
}

// 修复handleMeasure函数
const handleMeasure = () => {
  console.log('点击测量按钮，当前模式:', interactionMode.value)

  if (interactionMode.value === 'measure') {
    // 如果已经在测量模式，退出到空模式
    switchToMode(null)
  } else {
    // 进入测量模式
    switchToMode('measure')
  }
}

// 添加缩放状态标志
const isZooming = ref(false);

// 更新handleMouseMove函数以支持缩放功能
const handleMouseMove = (event: MouseEvent) => {
  if (!chartContainer.value || !svg || defaultData.value.length === 0) return
  
  let x = event.offsetX
  const y = event.offsetY
  
  // 确保只在主图表区域内响应鼠标事件
  if (y > chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) {
    return // 忽略离散量区域和缩放带上的事件
  }

  // 获取图表的有效区域宽度
  const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
  const maxX = margin.left + chartWidth;
  
  // 确保x坐标在有效区域内
  if (x < margin.left) {
    x = margin.left;
  } else if (x > maxX) {
    x = maxX;
  }
  
  const timestamp = xScale.invert(x)
  
  // 多边形模式：更新临时连接线
  if (interactionMode.value === 'polygon' && polygonPoints.value.length > 0) {
    // 更新临时连接线，显示从最后一个点到当前鼠标位置的线
    updateTempLine(x, y)
    return
  }
  
  // 处理缩放拖动
  if (isZooming.value) {
    // 记录当前拖动结束点
    dragEndX = x
    dragEndY = y
    
    // 计算选择框位置和大小
    const zoomSelection = svg.select('.zoom-selection')
    if (!zoomSelection.empty()) {
      // 获取当前使用的缩放类型
      // 修复类型错误: 避免直接比较 interactionMode.value 和 'zoom'
      const currentZoomType = zoomType.value; // 默认使用zoomType的值
      
      if (currentZoomType === 'horizontal') {
        // 水平缩放模式：只更新宽度
        const minX = Math.min(dragStartX, dragEndX)
        const maxX = Math.max(dragStartX, dragEndX)
        zoomSelection
          .attr('x', minX)
          .attr('width', maxX - minX)
      } else if (currentZoomType === 'vertical') {
        // 垂直缩放模式：只更新高度
        const minY = Math.min(dragStartY, dragEndY)
        const maxY = Math.max(dragStartY, dragEndY)
        zoomSelection
          .attr('y', minY)
          .attr('height', maxY - minY)
      } else {
        // 矩形缩放模式：更新宽度和高度
        const minX = Math.min(dragStartX, dragEndX)
        const maxX = Math.max(dragStartX, dragEndX)
        const minY = Math.min(dragStartY, dragEndY)
        const maxY = Math.max(dragStartY, dragEndY)
        zoomSelection
          .attr('x', minX)
          .attr('y', minY)
          .attr('width', maxX - minX)
          .attr('height', maxY - minY)
      }
    }
    return;
  }
  
  // 只有当isDragging为true时才执行游标拖动操作
  if (isDragging.value) {
    // 记录当前拖动结束点
    dragEndX = x
    dragEndY = y

    // 测量模式下的特殊处理
    if (interactionMode.value === 'measure' && isMeasuring.value) {
      // 测量模式：移动当前激活的测量游标
      const alignedTimestamp = alignToTimeInterval(timestamp);
      // const alignedTimestamp = Number(timestamp.toFixed(1))

      if (activeCursor.value === 'cursor1') {
        cursor1Position.value = alignedTimestamp;
      } else {
        cursor2Position.value = alignedTimestamp;
      }
      
      // 更新测量游标显示和数据
      updateMeasurementCursors();
      calculateMeasurementData();
    } else {
      // 普通游标模式：更新游标位置
      updateCursorPosition(x, timestamp);
    }
  }
}

// 获取游标位置对应的数据点
const getCursorData = (timestamp: number | Date) => {
  if (timestamp === undefined || timestamp === null) return {}

  // 在RJ模式下，将Date转换为毫秒数进行统一处理
  let numericTimestamp: number;
  numericTimestamp = timestamp instanceof Date ? timestamp.getTime() : new Date(timestamp as string).getTime();

  // 创建结果对象
  const result: Record<string, Record<string, { value: number | string, timestamp: number, color: string }>> = {}
  
  // 获取选中的参数列表
  const selectedParams = selectedParametersList.value || []
  
  // 如果timestamp小于等于0，检查是否在最左侧边缘
  const isAtLeftEdge = numericTimestamp <= 0;

  // 遍历每个参数在每个通道的当前值
  selectedParams.forEach(paramName => {
    defaultData.value.forEach(channel => {
      const channelId = channel.channelId
      const paramData = channel.parameters[paramName]
      // 如果结果对象中没有该通道，创建一个
      if (!result[channelId]) {
        result[channelId] = {}
      }
      if (!paramData || !Array.isArray(paramData) || paramData.length === 0) {
        // 设置此参数在此通道的值和时间戳以及颜色
        result[channelId][paramName] = {
          value: '-',
          timestamp: '-',
          color: getChannelColor(channelId, paramName)
        }
        return
      }
      let value: number | string;
      
      // 如果在最左侧边缘且有数据，使用第一个数据点
      if (isAtLeftEdge && paramData.length > 0) {
        value = paramData[0].value;
      } else {
        // 使用线性插值法计算游标位置的值，针对RJ模式进行适配
        // 找到游标左右两侧最近的点
        // 初始化左右点
        let leftPoint = null;
        let rightPoint = null;
        
        // 遍历查找左右点
        for (const point of paramData) {
          const timestamp = new Date(point.timestamp as string).getTime()
          if (timestamp <= numericTimestamp) {
            leftPoint = point;
          } else {
            rightPoint = point;
            break;
          }
        }
        
        // 处理边界情况
        if (!leftPoint && rightPoint) leftPoint = rightPoint;
        if (!rightPoint && leftPoint) rightPoint = leftPoint;

        // 获取左右点的时间戳数值
        const leftTimestamp = new Date(leftPoint.timestamp as string).getTime();
        const rightTimestamp = new Date(rightPoint.timestamp as string).getTime();
        
        // 计算插值
        if (leftTimestamp === new Date('1900-01-01 00:00:00').getTime()) {
          // 如果没有找到左侧点，使用右侧点的值
          value = rightPoint.value
        } else if (rightTimestamp === new Date('2200-01-01 00:00:00').getTime()) {
          // 如果没有找到右侧点，使用左侧点的值
          value = '-'
        } else if (leftTimestamp === rightTimestamp) {
          // 如果找到的是同一个点，直接使用该值
          value = leftPoint.value
        } else {
          // 区分连续量离散量 根据selectedParamsInfo.value中type判断 1为离散量 0为连续量
          const showType = selectedParamsInfo.value.find(item => item.name === paramName)?.type
          if (showType === 1) {
            value = leftPoint.value
          } else {
            // 线性插值
            const ratio = (numericTimestamp - leftTimestamp) / (rightTimestamp - leftTimestamp)
            //TODO 如果value是日期，需要计算中间日期
            if (typeof leftPoint.value === 'string' && leftPoint.value.includes('T')) {
              const leftDate = new Date(leftPoint.value)
              const rightDate = new Date(rightPoint.value)
              const middleDate = new Date(leftDate.getTime() + ratio * (rightDate.getTime() - leftDate.getTime()))
              value = middleDate.toISOString()
            } else {
              if (rightPoint.value == 'NaN' || leftPoint.value == 'NaN') {
                value = 'NaN'
              } else {
                value = leftPoint.value + ratio * (rightPoint.value - leftPoint.value)
              }
            }
          }
        }
      }
      
      // 获取该通道该参数的颜色
      const color = getChannelColor(channelId, paramName);
      
      // 设置此参数在此通道的值和时间戳以及颜色
      result[channelId][paramName] = {
        value,
        timestamp: numericTimestamp,
        color
      }
    })
  })
  
  return result
}

// 添加一个函数，将时间戳对齐到timeInterval的倍数
const alignToTimeInterval = (timestamp: number | Date): number => {
  // 在RJ模式下，将Date转换为毫秒数进行统一处理
  let numericTimestamp: number;
  numericTimestamp = timestamp instanceof Date ? timestamp.getTime() : new Date(timestamp as string).getTime();
  const interval = getTimeStep();
  // 四舍五入到最近的interval倍数
  const alignedValue = Math.round(numericTimestamp / interval) * interval;
  // 精确到两位小数，避免浮点误差
  return Number(alignedValue.toFixed(3));
}

// 更新游标位置函数
const updateCursorPosition = (x: number, timestamp: number | Date) => {
  if (!svg || !chartContainer.value) return
  // 在RJ模式下，将Date转换为毫秒数进行统一处理
  let numericTimestamp: number;
  numericTimestamp = timestamp instanceof Date ? timestamp.getTime() : new Date(timestamp as string).getTime();
  
  // 获取图表有效区域的宽度
  const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
  const maxX = margin.left + chartWidth;
  
  // 确保x坐标在有效区域内
  if (x < margin.left) {
    x = margin.left;
    const invertedValue = xScale.invert(x);
    numericTimestamp = (invertedValue as Date).getTime();
  } else if (x > maxX) {
    x = maxX;
    const invertedValue = xScale.invert(x);
    numericTimestamp = (invertedValue as Date).getTime();
  }
  
  // 检查x坐标是否为NaN或无限值，如果是则使用当前x轴区域的中点
  if (isNaN(x) || !isFinite(x)) {
    console.warn('游标X坐标无效:', x, '时间戳:', timestamp);
    const domain = xScale.domain();
    const midTime = new Date(((domain[0] as Date).getTime() + (domain[1] as Date).getTime()) / 2);
    numericTimestamp = midTime.getTime();
    x = xScale(midTime);
    
    // 如果仍然无效，则放弃绘制游标
    if (isNaN(x) || !isFinite(x)) {
      console.error('无法确定有效的游标位置');
      return;
    }
  }
  
  // 计算对齐到时间间隔的时间戳
  const alignedTimestamp = alignToTimeInterval(numericTimestamp)
  // const alignedTimestamp = Number(timestamp.toFixed(1))
  
  // 更新游标在屏幕上的相对位置比例
  if (chartContainer.value) {
    const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
    cursorScreenPositionRatio.value = (x - margin.left) / chartWidth;
    console.log('更新游标屏幕位置比例:', cursorScreenPositionRatio.value);
  }
  
  // 移除现有的游标线
  svg.selectAll('.cursor-line').remove()
  svg.selectAll('.cursor-group').remove()
  svg.selectAll('.discrete-cursor-line').remove() // 确保移除所有离散区域游标线
  
  // 移除可滚动容器中的游标线
  const scrollContainer = document.querySelector('.discrete-scroll-container');
  if (scrollContainer) {
    const innerSvg = scrollContainer.querySelector('.discrete-inner-svg');
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
    }
  }
  
  const selectedParams = selectedParametersList.value || []
  if (!selectedParams || selectedParams.length === 0) {
    // 如果没有选择任何参数，移除所有游标线和游标点
    svg.selectAll('.cursor-group').remove()
    svg.selectAll('.discrete-cursor-line').remove()
    svg.selectAll('.cursor-points-group').remove()
    
    // 同时移除测量游标线
    svg.selectAll('.cursor-line-1').remove()
    svg.selectAll('.cursor-line-2').remove()
    svg.selectAll('.measurement-cursor-group').remove()
    
    // 重置缩放窗口位置
    resetZoomWindowPosition()
    
    // 保存测量状态，但不改变游标位置，以便恢复
    
    return
  }
  
  // 如果处于测量模式，不使用普通游标
  if (isMeasuring.value) {
    svg.select('.cursor-line').style('display', 'none')
    svg.selectAll('.cursor-points-group').remove()
    svg.selectAll('.measurement-cursor-group').remove()
    svg.selectAll('.discrete-cursor-line').remove() // 确保清除离散区域游标线
    
    // 确保滚动容器中的游标线也被清除
    const scrollContainer = document.querySelector('.discrete-scroll-container');
    if (scrollContainer) {
      const innerSvg = scrollContainer.querySelector('.discrete-inner-svg');
      if (innerSvg) {
        d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
      }
    }
    
    // 更新缩放窗口位置
    updateZoomWindowPosition()
    
    // 保存测量状态，但不改变游标位置，以便恢复
    
    return
  }
  
  // 保存当前游标位置，以便在参数重新选择时可以恢复
  currentCursorPosition.value = alignedTimestamp
  
  // 计算对齐后的坐标位置
  const alignedX = xScale(alignedTimestamp);
  
  // 检查是否有任何连续显示模式的参数被选中
  const hasContinuousParams = selectedParams.some(paramName => {
    return ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].some(channelId => {
      return getChannelSettingDisplayMode(paramName, channelId) === 'continuous' &&
             getChannelSettingVisible(paramName, channelId)
    })
  })

  // 无论模式如何，都始终显示游标线
  if (hasContinuousParams) {
    // 移除现有游标和游标点
    svg.selectAll('.cursor-group').remove()
    svg.selectAll('.cursor-points-group').remove()
    
    // 创建新的游标组并应用裁剪
    const cursorGroup = svg.append('g')
      .attr('class', 'cursor-group')
      .attr('clip-path', 'url(#chart-area)')
    
    // 添加游标线
    cursorGroup.append('line')
      .attr('class', 'cursor-line')
      .attr('x1', alignedX)
      .attr('x2', alignedX)
      .attr('y1', margin.top)
      .attr('y2', chartContainer.value!.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) // 只延伸到主图表区域
      .attr('stroke', '#1890ff') // 使用主题蓝色
      .attr('stroke-width', 1)
      .style('display', 'block')
      .style('cursor', 'move') // 鼠标样式为可移动
      .on('mouseover', function() {
        // 鼠标悬停时改变游标样式
        d3.select(this)
          .attr('stroke-width', 2)
          .attr('stroke', '#3b82f6')
      })
      .on('mouseout', function() {
        // 鼠标离开时恢复游标样式
        d3.select(this)
          .attr('stroke-width', 1)
          .attr('stroke', 'var(--text-color)')
      })
      // 添加游标拖动功能
      .on('mousedown', function(event) {
        event.stopPropagation()
        isDragging.value = true
      })
    
    // 创建普通游标的渐变
    const normalGradientId = 'normalHandleGradient';
    let normalGradient = svg.select(`#${normalGradientId}`);
    
    if (normalGradient.empty()) {
      normalGradient = svg.append('defs')
        .append('linearGradient')
        .attr('id', normalGradientId)
        .attr('x1', '0%')
        .attr('y1', '0%')
        .attr('x2', '0%')
        .attr('y2', '100%');
        
      normalGradient.append('stop')
        .attr('offset', '0%')
        .attr('stop-color', '#4f46e5')
        .attr('stop-opacity', 1);
        
      normalGradient.append('stop')
        .attr('offset', '100%')
        .attr('stop-color', '#3730a3')
        .attr('stop-opacity', 1);
    }
    
    // 添加游标手柄的背景阴影
    cursorGroup.append('rect')
      .attr('class', 'cursor-handle-shadow')
      .attr('x', alignedX - 9)
      .attr('y', margin.top + 7)
      .attr('width', 18)
      .attr('height', 27)
      .attr('fill', 'rgba(0, 0, 0, 0.15)')
      .attr('rx', 6)
      .attr('ry', 6);
    
    // 添加游标手柄图标，表示可拖动
    cursorGroup.append('rect')
      .attr('class', 'cursor-handle')
      .attr('x', alignedX - 8)
      .attr('y', margin.top + 5)
      .attr('width', 16)
      .attr('height', 26)
      .attr('fill', `url(#${normalGradientId})`)
      .attr('stroke', '#3730a3')
      .attr('stroke-width', 1)
      .attr('rx', 5)
      .attr('ry', 5)
      .style('cursor', 'move')
      .style('display', 'block')
      .on('mouseover', function() {
        d3.select(this)
          .attr('stroke-width', 2)
          .attr('width', 18)
          .attr('x', alignedX - 9)
          .attr('height', 28)
        
        d3.select('.cursor-handle-shadow')
          .attr('width', 20)
          .attr('height', 30)
          .attr('x', alignedX - 10)
      })
      .on('mouseout', function() {
        d3.select(this)
          .attr('stroke-width', 1)
          .attr('width', 16)
          .attr('x', alignedX - 8)
          .attr('height', 26)
        
        d3.select('.cursor-handle-shadow')
          .attr('width', 18)
          .attr('height', 27)
          .attr('x', alignedX - 9)
      })
      .on('mousedown', function(event) {
        event.stopPropagation()
        isDragging.value = true
      })
    
    // 在普通游标手柄中添加三条线条作为指示图案
    for (let i = 0; i < 3; i++) {
      cursorGroup.append('line')
        .attr('x1', alignedX - 4)
        .attr('y1', margin.top + 11 + i * 5)
        .attr('x2', alignedX + 4)
        .attr('y2', margin.top + 11 + i * 5)
        .attr('stroke', 'white')
        .attr('stroke-width', 1.5)
        .attr('stroke-linecap', 'round');
    }
  } else {
    // 如果没有连续参数，移除曲线区域的游标线
    svg.select('.cursor-line').remove()
    svg.select('.cursor-handle').remove()
  }

  // 检查是否有任何离散显示模式的参数被选中
  const hasDiscreteParams = selectedParams.some(paramName => {
    // 检查该参数是否在任何通道中设置为离散显示
    return ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].some(channelId => {
      return getChannelSettingDisplayMode(paramName, channelId) === 'discrete' &&
             getChannelSettingVisible(paramName, channelId)
    })
  })

  // 只有在有离散参数时才更新离散区域的游标线
  if (hasDiscreteParams) {
    // 更新在离散量区域中的游标线
    addCursorToDiscreteArea(alignedTimestamp)
  } else {
    // 移除离散区域的游标线
    svg.selectAll('.discrete-cursor-line').remove()
    
    // 移除可滚动容器中的游标线
    const scrollContainer = document.querySelector('.discrete-scroll-container');
    if (scrollContainer) {
      const innerSvg = scrollContainer.querySelector('.discrete-inner-svg');
      if (innerSvg) {
        d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
      }
    }
  }
  // 更新故障带区域中的游标线
  const hasFaultBand = currentSelectedFaultIds.value.length > 0
  if(!hasFaultBand) {
    // 移除故障带区域中的游标线
    svg.selectAll('.fault-cursor-line').remove()
    // 移除可滚动容器中的游标线
    const scrollContainer = document.querySelector('.fault-scroll-container');
    if (scrollContainer) {
      const innerSvg = scrollContainer.querySelector('.fault-inner-svg');
      if (innerSvg) {
        d3.select(innerSvg).selectAll('.fault-cursor-line').remove();
      }
    }
  } else {
    // 更新在故障带区域中的游标线
    addCursorToFaultArea(alignedTimestamp)
  }

  
  // 更新游标点 - 确保在矩形缩放模式下也正确显示
  updateCursorPoints(alignedTimestamp)

  // 获取当前游标位置的数据
  const cursorData = getCursorData(alignedTimestamp)
  
  // 发送游标位置和对应的数据点
  emit('update:cursorPosition', alignedTimestamp)
  emit('update:cursorData', cursorData)
  
  // 更新游标标签
  updateCursorLabels()
}

const handleMouseLeave = () => {
  // 鼠标离开不会隐藏游标，游标保持在最后位置
}

// 鼠标按下处理函数
const handleMouseDown = (event: MouseEvent) => {
  if (!chartContainer.value || !svg || defaultData.value.length === 0) return
  
  // 设置焦点以接收键盘事件
  setFocus()
  
  const x = event.offsetX
  const y = event.offsetY
  
  // 确保只在主图表区域内响应鼠标事件
  if (y > chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) {
    return // 忽略离散量区域和缩放带上的事件
  }
  
  // 记录拖动开始位置
  dragStartX = event.offsetX
  dragEndX = event.offsetX
  dragStartY = event.offsetY
  dragEndY = event.offsetY
  
  // 根据不同的交互模式处理事件
  if (interactionMode.value === 'measure' && isMeasuring.value) {
    // 测量模式下，点击空白区域不再直接设置游标位置
    // 但允许进行缩放操作，所以这里不要直接返回
    // 继续执行后面的代码来开始缩放
  }
  
  // 如果没有拖动游标，则开始缩放操作 - 默认允许任何模式下点击空白处都可以缩放
  if (!isDragging.value) {
    // 开始缩放操作
    handleZoomStart(x, y);
  }
}

// 提取处理缩放开始的逻辑为一个单独的函数
const handleZoomStart = (x: number, y: number) => {
  // 开始缩放操作
  isZooming.value = true;
  
  // 保存当前拖动起始点
  dragStartX = x;
  dragStartY = y;
  dragEndX = x;
  dragEndY = y;
  
  // 修复：如果是水平缩放且鼠标从y轴左侧开始移动，自动将拖动起点设为左边缘
  // 这样用户可以选择到最左侧的数据点（从0开始）
  if (zoomType.value === 'horizontal' && (x <= margin.left)) {
    dragStartX = margin.left;
    dragEndX = margin.left; // 确保初始状态一致
  }
  
  // 在缩放前保存测量游标在屏幕上的相对位置（只在测量模式下）
  if (interactionMode.value === 'measure' && isMeasuring.value && chartContainer.value) {
    const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
    cursor1ScreenRatio.value = (xScale(cursor1Position.value) - margin.left) / chartWidth;
    cursor2ScreenRatio.value = (xScale(cursor2Position.value) - margin.left) / chartWidth;
    console.log('保存测量游标屏幕位置比例:', cursor1ScreenRatio.value, cursor2ScreenRatio.value);
  }
  
  // 清除已存在的选择框
  svg.selectAll('.zoom-selection').remove();
  
  // 获取当前使用的缩放类型
  const currentZoomType = zoomType.value;
  
  // 创建选择框
  let initialHeight = 0;
  let initialWidth = 0;
  
  if (currentZoomType === 'horizontal') {
    // 水平缩放，选择框高度为图表高度
    initialHeight = chartContainer.value ? 
      chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.top - margin.bottom : 
      0;
  } else if (currentZoomType === 'vertical') {
    // 垂直缩放，选择框宽度为图表宽度
    initialWidth = chartContainer.value ? 
      chartContainer.value.clientWidth - margin.left - margin.right : 
      0;
  }
  
  svg.append('rect')
    .attr('class', 'zoom-selection')
    .attr('x', currentZoomType === 'vertical' ? margin.left : x)
    .attr('y', margin.top)
    .attr('width', initialWidth)
    .attr('height', initialHeight)
    .attr('fill', 'rgba(173, 216, 230, 0.5)')
    .attr('stroke', 'blue')
    .attr('stroke-width', 1.5);
}

// 保存缩放历史记录，用于回退
const zoomHistory = ref<{xDomain: [number, number], yDomain: [number, number], rectZoomYRanges?: Record<string, [number, number]>}[]>([]);

// 在初始化完成后添加初始状态到历史记录
const initChartAndCursor = () => {
  initChart()
  
  // 设置游标初始位置为X轴范围的中点(50%处)
  const [xMin, xMax] = xScale.domain()
  let initialPosition = 0
  if (xMin === Infinity && xMax === -Infinity) {
    initialPosition = 0.1 // 设置为x轴范围的50%处
    currentCursorPosition.value = initialPosition
  } else {
    initialPosition = (xMin + xMax) / 2 // 设置为x轴范围的中点
    currentCursorPosition.value = initialPosition
  }
  
  // 设置初始游标屏幕位置比例为0.5(50%，即屏幕中间)
  cursorScreenPositionRatio.value = 0.5;
  
  // 确保初始化后立即发送游标位置
  setTimeout(() => {
    const initialX = xScale(initialPosition)
    emit('update:cursorPosition', initialPosition)
    updateCursorPosition(initialX, initialPosition)
    updateCursorPoints(initialPosition)
  }, 100)
}
const updateMeasurementResults = async () => {
  // 获取游标位置，转换为数值类型用于比较
  let cursor1: number;
  let cursor2: number;
  
  // RJ模式：安全地处理各种时间格式
  if (cursor1Position.value && typeof cursor1Position.value === 'object' && (cursor1Position.value as any).getTime) {
    cursor1 = (cursor1Position.value as Date).getTime();
  } else if (typeof cursor1Position.value === 'number') {
    cursor1 = cursor1Position.value < 10000000000 ? cursor1Position.value * 1000 : cursor1Position.value;
  } else if (typeof cursor1Position.value === 'string') {
    cursor1 = new Date(cursor1Position.value).getTime();
  } else {
    cursor1 = Date.now();
  }
  
  if (cursor2Position.value && typeof cursor2Position.value === 'object' && (cursor2Position.value as any).getTime) {
    cursor2 = (cursor2Position.value as Date).getTime();
  } else if (typeof cursor2Position.value === 'number') {
    cursor2 = cursor2Position.value < 10000000000 ? cursor2Position.value * 1000 : cursor2Position.value;
  } else if (typeof cursor2Position.value === 'string') {
    cursor2 = new Date(cursor2Position.value).getTime();
  } else {
    cursor2 = Date.now();
  }
  let deltaTime = 0
  deltaTime = Number((Math.abs(cursor1 - cursor2) / 1000).toFixed(0))
  // 创建结果对象
  const measurementResults: Record<string, {
    parameterId: string,
    channelId: string,
    min: number,
    max: number,
    avg: number,
    fluctuation: number
  }> = {}
  let measurementResultsTemp: Record<string, {
    parameterId: string,
    channelId: string,
    min: number,
    max: number,
    avg: number,
    fluctuation: number
  }> = {}
  // TODO从接口获取测量数据
  // 将cursor1，cursor2由时间戳转换为日期字符串
  const pad = (n: number) => n < 10 ? '0' + n : n;
  const formatDate = (date: number) => {
    const d = new Date(date);
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  };
  let cursor1Date = '';
  let cursor2Date = '';
  if (cursor1 < cursor2) {
    cursor1Date = formatDate(cursor1);
    cursor2Date = formatDate(cursor2);
  } else {
    cursor1Date = formatDate(cursor2);
    cursor2Date = formatDate(cursor1);
  }
  
  let columnList = selectedParamsInfo.value
  // 将 columnList 转换为 { id: name, id: name } 的对象格式
  const columnListObj = columnList.reduce((acc, item) => {
    acc[item.id] = item.name;
    return acc;
  }, {} as { [key: string]: string })
  
  // 设置测量数据loading状态
  isMeasurementLoading.value = true
  emit('update:measurementLoading', true)
  
  try {
    if (columnList.length == 0) {
      measurementResultsTemp = {};
    } else {
      let params = {
        unitCode: unitCode.value,
        startTime: cursor1Date,
        endTime: cursor2Date,
        params: columnListObj,
      };
      const res = await API.queryMeasurementData(params);
      if (res && res.code === 200) {
        measurementResultsTemp = res.result || res.data || {};
      } else {
        measurementResultsTemp = {};
        console.warn("查询测量数据返回异常，code:", res?.code);
        messageRef.value?.show({
          type: "warning",
          content: res?.message || "测量数据获取失败",
          duration: 3000,
        });
      }
    }
  } catch (error: any) {
    measurementResultsTemp = {};
    console.error("Failed to fetch measurement data:", error);
    messageRef.value?.show({
      type: "error",
      content: error.response?.data?.msg || error.message || "测量数据获取失败",
      duration: 3000,
    });
  } finally {
    // 无论成功或失败，都关闭loading状态
    isMeasurementLoading.value = false
    emit('update:measurementLoading', false)
  }
  Object.keys(measurementResultsTemp).forEach(key => {
    selectedParamsInfo.value.forEach(param => {
      if (param.id === key) {
        measurementResults[param.name] = measurementResultsTemp[key]
      }
    })
  })
  Object.keys(measurementResults).forEach(key => {
    measurementResults[key].parameterId = key
    measurementResults[key].channelId = 'RJ'
  })
  emit('update:measurementData', {
    cursor1,
    cursor2,
    deltaTime,
    measurementResults,
  })
}
// 修改缩放操作，记录每次缩放状态并同步更新缩放带
const handleMouseUp = async () => {
  if (!chartContainer.value || !svg) return
  // 处理游标拖动结束
  if (isDragging.value) {
    isDragging.value = false
    
    // 如果在测量模式下结束拖动，确保离散区域的游标线也正确显示
    if (interactionMode.value === 'measure' && isMeasuring.value) {
      // 先清除所有离散区域游标线
      svg.selectAll('.discrete-cursor-line').remove();
      
      // 清除可滚动容器中的游标线
      const scrollContainer = document.querySelector('.discrete-scroll-container');
      if (scrollContainer) {
        const innerSvg = scrollContainer.querySelector('.discrete-inner-svg');
        if (innerSvg) {
          d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
        }
      }
      // 重新绘制测量游标，包括离散区域的
      updateMeasurementCursors();
      updateMeasurementResults();
    }
  }
  
  // 处理缩放拖动结束
  if (isZooming.value) {
    isZooming.value = false;
    
    // 执行缩放操作
    const zoomSelection = svg.select('.zoom-selection')
    if (!zoomSelection.empty()) {
      // 获取当前使用的缩放类型，直接使用zoomType的值
      const currentZoomType = zoomType.value;
      
      // 在缩放前记录游标在屏幕上的相对位置
      if (currentCursorPosition.value !== null && chartContainer.value) {
        const cursorX = xScale(currentCursorPosition.value);
        const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
        cursorScreenPositionRatio.value = (cursorX - margin.left) / chartWidth;
        console.log('记录游标屏幕位置比例:', cursorScreenPositionRatio.value);
      }
      
      // 水平缩放
      if (currentZoomType === 'horizontal') {
        // 确保拖动宽度足够，防止误操作
        if (Math.abs(dragEndX - dragStartX) > 5) {
          isLoading.value = true
          // 如果向右拖动，进行缩放
          if (dragEndX > dragStartX) {
            // 计算新的范围
            let minX = Math.min(dragStartX, dragEndX)
            const maxX = Math.max(dragStartX, dragEndX)
            
            // 确保minX至少为margin.left
            minX = Math.max(minX, margin.left)
            
            // 计算时间范围
            let startTime = xScale.invert(minX)
            const endTime = xScale.invert(maxX)
            
            // RJ模式下，如果起始点位于y轴左侧或就是y轴位置，将起始时间设为数据的最左侧起始时间
            if (minX === margin.left && dragStartX <= margin.left) {
              // RJ模式：获取数据的真实起始时间
              let minTime: Date | null = null;
              
              defaultData.value.forEach((channel: any) => {
                Object.entries(channel.parameters).forEach(([paramId, paramData]: [string, any]) => {
                  if (!paramData || !Array.isArray(paramData)) return
                  
                  if (paramData.length > 0) {
                    paramData.forEach((d: any) => {
                      const timestamp = new Date(d.timestamp);
                      if (!isNaN(timestamp.getTime())) {
                        if (!minTime || timestamp < minTime) {
                          minTime = timestamp;
                        }
                      }
                    });
                  }
                })
              })
              
              if (minTime) {
                startTime = minTime;
              }
            }
            
            console.log('水平缩放更新X轴范围:', [startTime, endTime])
            
            // 应用缩放 - 只修改x轴范围
            xScale.domain([startTime, endTime])
            
            // 保存当前所有参数的y轴范围
            const selectedParams = selectedParametersList.value?.filter(param => 
              param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
            ) || [];
            
            // 记录每个参数的Y轴范围以便保持不变
            const paramYScaleRanges: Record<string, [number, number]> = {};
            selectedParams.forEach(paramName => {
              if (getParameterDisplayMode(paramName) === 'continuous') {
                const paramYScale = paramYScaleMap.value[paramName];
                if (paramYScale) {
                  // 获取当前Y轴范围并保存
                  paramYScaleRanges[paramName] = paramYScale.domain() as [number, number];
                }
              }
            });
            
            // 设置矩形缩放模式标记和保存Y轴范围
            isRectZoomActive.value = true;
            rectZoomYRanges.value = paramYScaleRanges;

            // 设置标记，指示在更新图表时忽略用户设置的Y轴范围
            skipUserYAxisSettings.value = true;

            //获取数据更新defaultData
            // 请求接口获取绘图数据
            const params = prepareApiParams(startTime, endTime)
            
            // 请求接口获取绘图数据
            getApiMethod()(params).then((res: any) => {
              if (res && res.code === 200) {
                let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
                Object.keys(res.data).forEach(key => {
                  selectedParamsInfo.value.forEach((param) => {
                    if (param.id === key) {
                      resdataTemp[param.name] = res.data[key]
                    }
                  })
                })
                resdataTemp['RecTime'] = res.data['RecTime']
                defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
                timeIntervalList.value = []
                // 获取interval
                if (defaultData.value && defaultData.value.length > 0 ) {
                  defaultData.value.forEach((item: any) => {
                    timeIntervalList.value.push(item.interval)
                  })
                }
                // 更新坐标轴
                updateAxes()
                
                // 使用保持Y轴范围的更新方法，而不是重新计算Y轴范围
                updateChartWithParamYScales();
                
                // 确保游标线和游标点正确显示在相同的屏幕位置
                if (chartContainer.value) {
                  const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
                  const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
                  const newTimestamp = xScale.invert(newCursorX);
                  currentCursorPosition.value = newTimestamp;
                  
                  // 根据模式更新游标
                  if (interactionMode.value === 'measure' && isMeasuring.value) {
                    // 使用保存的屏幕位置比例计算新的游标位置
                    const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
                    const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
                    
                    cursor1Position.value = xScale.invert(cursor1X);
                    cursor2Position.value = xScale.invert(cursor2X);
                    
                    // 更新测量游标和测量数据
                    updateMeasurementCursors();
                    updateMeasurementResults();
                  } else {
                    updateCursorPosition(newCursorX, newTimestamp);
                  }
                  
                  console.log('缩放后更新游标到屏幕位置比例:', cursorScreenPositionRatio.value);
                }
                // 更新标注高亮
                updateAnnotationHighlights();

                // 短暂延时后恢复标记
                setTimeout(() => {
                  skipUserYAxisSettings.value = false;
                  isLoading.value = false
                }, 100);
                // 记录当前状态到历史记录
                recordZoomState();
              } else {
                isLoading.value = false
              }
            }).catch((error: any) => {
              isLoading.value = false
              messageRef.value?.show({
                type: 'warning',
                content: error.response.data.msg,
                duration: 3000
              })
            })
          } else if (dragEndX < dragStartX) {
            // 向左拖动 - 恢复上次视图
            undoZoom();
            
            // 确保游标线和游标点正确显示在相同的屏幕位置
            if (chartContainer.value) {
              const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
              const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
              const newTimestamp = xScale.invert(newCursorX);
              currentCursorPosition.value = newTimestamp;
              
              // 根据模式更新游标
              if (interactionMode.value === 'measure' && isMeasuring.value) {
                // 使用保存的屏幕位置比例计算新的游标位置
                const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
                const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
                
                cursor1Position.value = xScale.invert(cursor1X);
                cursor2Position.value = xScale.invert(cursor2X);
                
                // 更新测量游标和测量数据
                updateMeasurementCursors();
                updateMeasurementResults();
              } else {
                updateCursorPosition(newCursorX, newTimestamp);
              }
            }
          }
        }
      } else if (currentZoomType === 'vertical') {
        // 垂直缩放
        if (Math.abs(dragEndY - dragStartY) > 5) {
          isLoading.value = true
          // 如果向下拖动，进行垂直缩放
          if (dragEndY > dragStartY) {
            // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
            const selectedParams = selectedParametersList.value?.filter(param => 
              param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
            ) || [];
            
            // 记录每个参数的Y轴缩放范围
            const paramYScaleRanges: Record<string, [number, number]> = {};
            
            // 对每个选中的参数计算其Y轴缩放范围
            selectedParams.forEach(paramName => {
              if (getParameterDisplayMode(paramName) == 'continuous') {
                // 获取该参数的Y轴比例尺
                const paramYScale = paramYScaleMap.value[paramName];
                
                if (paramYScale) {
                  // 使用参数特定的Y轴比例尺转换屏幕坐标到数据值
                  // 注意：Y轴方向是反的，较小的Y坐标对应较大的数据值
                  const topValue = paramYScale.invert(Math.min(dragStartY, dragEndY));
                  const bottomValue = paramYScale.invert(Math.max(dragStartY, dragEndY));
                  
                  // 正确处理Y值的大小关系 - 在屏幕坐标系中，较小的Y值代表较大的数据值
                  paramYScaleRanges[paramName] = [
                    Math.min(bottomValue, topValue), // 数据空间中的最小值
                    Math.max(bottomValue, topValue)  // 数据空间中的最大值
                  ];
                } else {
                  // 如果没有找到参数特定的比例尺，使用全局Y轴（回退方案）
                  const topValue = yScale.invert(Math.min(dragStartY, dragEndY));
                  const bottomValue = yScale.invert(Math.max(dragStartY, dragEndY));
                  
                  // 正确处理Y值的大小关系
                  paramYScaleRanges[paramName] = [
                    Math.min(bottomValue, topValue),
                    Math.max(bottomValue, topValue)
                  ];
                }
              }
            });
            
            console.log('垂直缩放更新Y轴范围:', paramYScaleRanges);
            
            // 将参数Y轴范围保存到rectZoomYRanges中
            rectZoomYRanges.value = paramYScaleRanges;
            
            // 设置矩形缩放模式标记
            isRectZoomActive.value = true;
            
            // 保存当前缩放状态到历史记录
            recordZoomState();
            
            // 更新坐标轴
            updateAxes();
            
            // 更新图表，使用保存的Y轴范围
            updateChartWithParamYScales();
            // 更新标注高亮
            updateAnnotationHighlights();

            setTimeout(() => {
              isLoading.value = false
            }, 300)
          } else if (dragEndY < dragStartY) {
            // 向上拖动 - 恢复上次视图
            undoZoom();
            setTimeout(() => {
              isLoading.value = false
            }, 300)
          }
        }
      } else if (currentZoomType === 'rect') {
        // 矩形缩放
        if (Math.abs(dragEndX - dragStartX) > 5 && Math.abs(dragEndY - dragStartY) > 5) {
          isLoading.value = true
          if (dragEndX < dragStartX || dragEndY < dragStartY) {
            // 向左或向上拖动 - 恢复原始视图
            undoZoom();
            setTimeout(() => {
              isLoading.value = false
            }, 300)
          } else {
            // 确保拖动距离足够大，防止意外点击
            // 计算选择框对应的数据范围
            const minX = Math.min(dragStartX, dragEndX)
            const maxX = Math.max(dragStartX, dragEndX)
            const minY = Math.min(dragStartY, dragEndY)
            const maxY = Math.max(dragStartY, dragEndY)
            zoomSelection
              .attr('x', minX)
              .attr('y', minY)
              .attr('width', maxX - minX)
              .attr('height', maxY - minY)
              
            // 转换为数据范围
            let startTime = xScale.invert(minX)
            const endTime = xScale.invert(maxX)
            // RJ模式下，如果起始点位于y轴左侧或就是y轴位置，将起始时间设为数据的最左侧起始时间
            if (minX === margin.left && dragStartX <= margin.left) {
              // RJ模式：获取数据的真实起始时间
              let minTime: Date | null = null;
              
              defaultData.value.forEach((channel: any) => {
                Object.entries(channel.parameters).forEach(([paramId, paramData]: [string, any]) => {
                  if (!paramData || !Array.isArray(paramData)) return
                  
                  if (paramData.length > 0) {
                    paramData.forEach((d: any) => {
                      const timestamp = new Date(d.timestamp);
                      if (!isNaN(timestamp.getTime())) {
                        if (!minTime || timestamp < minTime) {
                          minTime = timestamp;
                        }
                      }
                    });
                  }
                })
              })
              
              if (minTime) {
                startTime = minTime;
              }
              
            }
            
            // 记录每个参数的Y轴缩放范围
            const paramYScaleRanges: Record<string, [number, number]> = {};
            
            // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
            const selectedParams = selectedParametersList.value?.filter(param => 
              param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
            ) || [];
            
            // 对每个选中的参数计算其Y轴缩放范围
            selectedParams.forEach(paramName => {
              if (getParameterDisplayMode(paramName) == 'continuous') {
                // 获取该参数的Y轴比例尺
                const paramYScale = paramYScaleMap.value[paramName];
                
                if (paramYScale) {
                  // 使用参数特定的Y轴比例尺转换屏幕坐标到数据值
                  // 注意：Y轴方向是反的，较小的Y坐标对应较大的数据值
                  const topValue = paramYScale.invert(minY);
                  const bottomValue = paramYScale.invert(maxY);
                  
                  // 正确处理Y值的大小关系 - 在屏幕坐标系中，较小的Y值代表较大的数据值
                  paramYScaleRanges[paramName] = [
                    Math.min(bottomValue, topValue), // 数据空间中的最小值
                    Math.max(bottomValue, topValue)  // 数据空间中的最大值
                  ];
                } else {
                  // 如果没有找到参数特定的比例尺，使用全局Y轴（回退方案）
                  const topValue = yScale.invert(minY);
                  const bottomValue = yScale.invert(maxY);
                  
                  // 正确处理Y值的大小关系
                  paramYScaleRanges[paramName] = [
                    Math.min(bottomValue, topValue),
                    Math.max(bottomValue, topValue)
                  ];
                }
              }
            });
            
            console.log('矩形缩放更新范围:', {
              xRange: [startTime, endTime],
              paramYScaleRanges
            });
            
            // 应用X轴缩放
            xScale.domain([startTime, endTime]);
            
            // 将参数Y轴范围保存到rectZoomYRanges中
            rectZoomYRanges.value = paramYScaleRanges;
            
            // 设置矩形缩放模式标记
            isRectZoomActive.value = true;
            
            // 更新坐标轴
            updateAxes();
            
            // 矩形缩放时重新获取数据
            const params = prepareApiParams(startTime, endTime)
            
            // 请求接口获取绘图数据
            getApiMethod()(params).then((res: any) => {
              if (res && res.code === 200) {
                // 更新数据
                let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
                Object.keys(res.data).forEach(key => {
                  selectedParamsInfo.value.forEach((param) => {
                    if (param.id === key) {
                      resdataTemp[param.name] = res.data[key]
                    }
                  })
                })
                resdataTemp['RecTime'] = res.data['RecTime']
                defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
                timeIntervalList.value = []
                // 获取interval
                if (defaultData.value && defaultData.value.length > 0 ) {
                  defaultData.value.forEach((item: any) => {
                    timeIntervalList.value.push(item.interval)
                  })
                }

                // 更新图表，使用保存的Y轴范围
                updateChartWithParamYScales();
                // 更新标注高亮
                updateAnnotationHighlights();


                // 重新绘制缩放带上的缩放窗口位置
                updateZoomWindowPosition()
                
                // 确保游标线和游标点正确显示在相同的屏幕位置
                if (chartContainer.value) {
                  const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
                  const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
                  const newTimestamp = xScale.invert(newCursorX);
                  currentCursorPosition.value = newTimestamp;

                  // 根据模式更新游标
                  if (interactionMode.value === 'measure' && isMeasuring.value) {
                    // 使用保存的屏幕位置比例计算新的测量游标位置
                    const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
                    const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
                    
                    cursor1Position.value = xScale.invert(cursor1X);
                    cursor2Position.value = xScale.invert(cursor2X);
                    
                    // 更新测量游标和测量数据
                    updateMeasurementCursors();
                    updateMeasurementResults();
                  } else {
                    updateCursorPosition(newCursorX, newTimestamp);
                  }
                }
                
                // 如果故障模式激活且有选中的故障，重新绘制故障线
                if (isFaultModeActive.value && currentSelectedFaultIds.value && currentSelectedFaultIds.value.length > 0 && currentFaultList.value) {
                  updateFaultDisplayMode('', currentSelectedFaultIds.value, currentFaultList.value)
                  updateFaultArea.value = false
                } else {
                  updateFaultArea.value = false
                }
              }
              
              // 记录当前状态到历史记录
              recordZoomState();
              setTimeout(() => {
                isLoading.value = false
              }, 300)
            }).catch(error => {
              console.error('矩形缩放获取数据出错:', error);
              messageRef.value?.show({
                type: 'warning',
                content: error.response.data.msg,
                duration: 3000
              })
              setTimeout(() => {
                isLoading.value = false
              }, 300)
            })
          }
        }
      }
    }
    
    // 移除选择框
    svg.selectAll('.zoom-selection').remove()
    
    // 确保在任何缩放操作后都更新游标位置到相同的屏幕位置
    if (chartContainer.value) {
      const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
      const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
      const newTimestamp = xScale.invert(newCursorX);
      currentCursorPosition.value = newTimestamp;
      // 根据当前交互模式更新游标
      if (interactionMode.value === 'measure' && isMeasuring.value) {
        // 使用保存的屏幕位置比例计算新的测量游标位置
        const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
        const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
        
        cursor1Position.value = xScale.invert(cursor1X);
        cursor2Position.value = xScale.invert(cursor2X);
        
        // 更新测量游标和测量数据
        updateMeasurementCursors();
        updateMeasurementResults();
      } else {
        updateCursorPosition(newCursorX, newTimestamp);
      }
      
      // 更新缩放带的窗口位置
      updateZoomWindowPosition();
    }
  }
}

// 记录当前缩放状态到历史记录
const recordZoomState = () => {
  const state = {
    xDomain: [...xScale.domain()] as [number, number],
    yDomain: [...yScale.domain()] as [number, number]
  };
  
  // 如果是矩形缩放，还需要保存Y轴范围信息
  if ((zoomType.value === 'rect' && rectZoomYRanges.value) || zoomType.value === 'vertical') {
    state.rectZoomYRanges = JSON.parse(JSON.stringify(rectZoomYRanges.value));
  }
  
  zoomHistory.value.push(state);
  
  // 限制历史记录长度，防止内存占用过大
  if (zoomHistory.value.length > 20) {
    zoomHistory.value.shift();
  }
}

// 添加一个变量跟踪最后一次按键事件的时间
let lastKeyEventTime = 0;
// 键盘事件处理相关变量
const keyboardMoveStep = ref(1) // 每次移动的步长比例，相对于当前缩放窗口宽度

// 修改键盘事件处理
const handleKeyDown = (event: KeyboardEvent) => {
  // 添加调试日志
  console.log('handleKeyDown 被调用, key:', event.key, '时间戳:', Date.now());
  
  // 检查当前焦点是否在输入框上，如果是则不处理左右箭头键事件
  const activeElement = document.activeElement;
  if (activeElement && (
    activeElement.tagName === 'INPUT' ||
    activeElement.tagName === 'TEXTAREA' ||
    activeElement.contentEditable === 'true'
  )) {
    // 如果焦点在输入框上，不处理左右箭头键事件，让输入框正常处理光标移动
    if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
      return;
    }
  }

  // 防止事件在短时间内重复触发（300毫秒内的相同键位事件将被忽略）
  const now = Date.now();
  if (now - lastKeyEventTime < 100 && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
    console.log('忽略重复的键盘事件, 间隔:', now - lastKeyEventTime);
    return;
  }
  lastKeyEventTime = now;
  
  if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
    if (event.shiftKey) {
      event.preventDefault() // 阻止默认的页面滚动

      // 获取当前缩放窗口的位置和宽度
      const currentZoomWindow = svg?.select('.zoom-window')
      if (currentZoomWindow.empty()) return

      const currentX = parseFloat(currentZoomWindow.attr('x') || '0')
      const currentWidth = parseFloat(currentZoomWindow.attr('width') || '0')

      if (currentWidth <= 0) return

      // 计算移动步长（相对于当前缩放窗口宽度）
      const moveDistance = currentWidth * keyboardMoveStep.value

      let newX = currentX
      if (event.key === 'ArrowRight') {
        // 向右移动
        newX = currentX + moveDistance
      } else if (event.key === 'ArrowLeft') {
        // 向左移动
        newX = currentX - moveDistance
      }

      // 获取缩放带的边界
      const zoomBarBackground = svg?.select('.zoom-bar-background')
      if (zoomBarBackground.empty()) return

      const zoomBarX = parseFloat(zoomBarBackground.attr('x') || '0')
      const zoomBarWidth = parseFloat(zoomBarBackground.attr('width') || '0')
      const maxX = zoomBarX + zoomBarWidth - currentWidth

      // 限制在概览图范围内
      newX = Math.max(zoomBarX, Math.min(newX, maxX))

      // 更新缩放窗口位置
      currentZoomWindow.attr('x', newX)
      // 同步更新左右手柄位置
      const leftHandle = svg?.select('.zoom-handle-left')
      const rightHandle = svg?.select('.zoom-handle-right')
      if (!leftHandle.empty()) {
        leftHandle.attr('x', newX - parseFloat(leftHandle.attr('width') || '0') / 2)
      }
      if (!rightHandle.empty()) {
        rightHandle.attr('x', newX + currentWidth - parseFloat(rightHandle.attr('width') || '0') / 2)
      }

      const zoomWindowLeft = parseFloat(currentZoomWindow.attr('x') || '0')
      const zoomWindowWidth = parseFloat(currentZoomWindow.attr('width') || '0')

      // 计算缩放窗口在缩放带中的位置比例
      const leftRatio = (zoomWindowLeft - margin.left) / zoomBarWidth
      const rightRatio = (zoomWindowLeft + zoomWindowWidth - margin.left) / zoomBarWidth

      // 获取缩放带的初始范围（全局数据范围）
      const [zoomBarMin, zoomBarMax] = initialDomain.value

      // 计算新的X轴范围
      let newMin: number | Date
      let newMax: number | Date

      // RJ模式：时间范围
      const totalTimeRange = (zoomBarMax as Date).getTime() - (zoomBarMin as Date).getTime()
      newMin = new Date((zoomBarMin as Date).getTime() + leftRatio * totalTimeRange)
      newMax = new Date((zoomBarMin as Date).getTime() + rightRatio * totalTimeRange)

      // 更新X轴范围
      xScale.domain([newMin, newMax])

      // 更新X轴显示
      if (svg) {
        svg.select('.x-axis').call(d3.axisBottom(xScale) as any)
      }

      // 查询新范围的数据
      const queryDataForNewRange = async (newMin: number | Date, newMax: number | Date) => {
        try {
          console.log('查询新范围数据:', { newMin, newMax })

          // 准备API参数
          const params = prepareApiParams(newMin, newMax)

          // 调用API获取新范围的数据
          const res = await getApiMethod()(params)

          if (res && res.code === 200) {
            // 更新数据
            let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
            Object.keys(res.data).forEach(key => {
              selectedParamsInfo.value.forEach((param) => {
                if (param.id === key) {
                  resdataTemp[param.name] = res.data[key]
                }
              })
            })
            resdataTemp['RecTime'] = res.data['RecTime']
            defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];

            timeIntervalList.value = []
            // 获取interval
            if (defaultData.value && defaultData.value.length > 0 ) {
              defaultData.value.forEach((item: any) => {
                timeIntervalList.value.push(item.interval)
              })
            }

            // 保存当前所有参数的y轴范围
            const selectedParams = selectedParametersList.value?.filter(param =>
              param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
            ) || [];

            // 记录每个参数的Y轴范围以便保持不变
            const paramYScaleRanges: Record<string, [number, number]> = {};
            selectedParams.forEach(paramName => {
              if (getParameterDisplayMode(paramName) === 'continuous') {
                const paramYScale = paramYScaleMap.value[paramName];
                if (paramYScale) {
                  // 获取当前Y轴范围并保存
                  paramYScaleRanges[paramName] = paramYScale.domain() as [number, number];
                }
              }
            });

            // 设置矩形缩放模式标记和保存Y轴范围
            isRectZoomActive.value = true;
            rectZoomYRanges.value = paramYScaleRanges;

            // 设置标记，指示在更新图表时忽略用户设置的Y轴范围
            skipUserYAxisSettings.value = true;

            // 更新图表显示
            updateChartWithParamYScales()
            // 确保游标线和游标点正确显示在相同的屏幕位置
            if (chartContainer.value) {
              const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
              const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
              const newTimestamp = xScale.invert(newCursorX);
              currentCursorPosition.value = newTimestamp;

              // 根据模式更新游标
              if (interactionMode.value === 'measure' && isMeasuring.value) {
                // 使用保存的屏幕位置比例计算新的游标位置
                const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
                const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);

                cursor1Position.value = xScale.invert(cursor1X);
                cursor2Position.value = xScale.invert(cursor2X);

                // 更新测量游标和测量数据
                updateMeasurementCursors();
                updateMeasurementResults();
              } else {
                updateCursorPosition(newCursorX, newTimestamp);
              }

              console.log('缩放后更新游标到屏幕位置比例:', cursorScreenPositionRatio.value);
            }
            // 更新标注高亮
            updateAnnotationHighlights();

            // 记录当前状态到历史记录
            recordZoomState();
          }

        } catch (error) {
          console.error('查询新范围数据失败:', error)
        }
      }

      // 调用数据查询接口重新绘图
      queryDataForNewRange(newMin, newMax)
    } else {
      if (isMeasuring.value) {
        // 测量模式下的键盘控制保持不变
        event.preventDefault(); // 防止页面滚动
        event.stopPropagation(); // 防止事件冒泡

        const direction = event.key === 'ArrowLeft' ? -1 : 1;
        const [xMin, xMax] = xScale.domain();
        let stepSize = getTimeStep();

        // RJ模式下，步长需要转换为毫秒（因为游标位置使用Date对象，内部是毫秒时间戳）
        stepSize = stepSize * 1000; // 将秒转换为毫秒

        if (activeCursor.value === 'cursor1') {
      
          // RJ模式：处理Date对象
          let currentTime: number;
          if (cursor1Position.value && typeof cursor1Position.value === 'object' && (cursor1Position.value as any).getTime) {
            currentTime = (cursor1Position.value as Date).getTime();
          } else if (typeof cursor1Position.value === 'number') {
            currentTime = cursor1Position.value < 10000000000 ? cursor1Position.value * 1000 : cursor1Position.value;
          } else if (typeof cursor1Position.value === 'string') {
            currentTime = new Date(cursor1Position.value).getTime();
          } else {
            currentTime = Date.now();
          }
          const newTime = Math.max((xMin as Date).getTime(), Math.min((xMax as Date).getTime(), currentTime + direction * stepSize));
          cursor1Position.value = new Date(newTime);
     
          updateMeasurementCursors();
          updateMeasurementResults();
        } else {
          // RJ模式：处理Date对象
          let currentTime: number;
          if (cursor2Position.value && typeof cursor2Position.value === 'object' && (cursor2Position.value as any).getTime) {
            currentTime = (cursor2Position.value as Date).getTime();
          } else if (typeof cursor2Position.value === 'number') {
            currentTime = cursor2Position.value < 10000000000 ? cursor2Position.value * 1000 : cursor2Position.value;
          } else if (typeof cursor2Position.value === 'string') {
            currentTime = new Date(cursor2Position.value).getTime();
          } else {
            currentTime = Date.now();
          }
          const newTime = Math.max((xMin as Date).getTime(), Math.min((xMax as Date).getTime(), currentTime + direction * stepSize));
          cursor2Position.value = new Date(newTime);
       
          updateMeasurementCursors();
          updateMeasurementResults();
        }
      } else {
        // 普通游标模式下，使用数据时间间隔移动游标
        event.preventDefault(); // 防止页面滚动
        event.stopPropagation(); // 防止事件冒泡

        const direction = event.key === 'ArrowLeft' ? -1 : 1;
        let stepSize = getTimeStep(); // 获取数据时间间隔

        // RJ模式下，步长需要转换为毫秒（因为普通游标位置在RJ模式下也使用Date对象）
        stepSize = stepSize * 1000; // 将秒转换为毫秒

        // 根据数据时间间隔移动游标位置
        if (currentCursorPosition.value !== null) {
          const [xMin, xMax] = xScale.domain();
          let newTimestamp: number | Date;

          // RJ模式：处理Date对象
          let currentTime: number;
          if (currentCursorPosition.value && typeof currentCursorPosition.value === 'object' && (currentCursorPosition.value as any).getTime) {
            currentTime = (currentCursorPosition.value as Date).getTime();
          } else if (typeof currentCursorPosition.value === 'number') {
            // 如果是数字，判断是秒还是毫秒
            currentTime = currentCursorPosition.value < 10000000000 ? currentCursorPosition.value * 1000 : currentCursorPosition.value;
          } else if (typeof currentCursorPosition.value === 'string') {
            currentTime = new Date(currentCursorPosition.value).getTime();
          } else {
            // 使用当前时间作为fallback
            currentTime = Date.now();
          }
          const newTime = Math.max((xMin as Date).getTime(), Math.min((xMax as Date).getTime(), currentTime + direction * stepSize));
          newTimestamp = new Date(newTime);

          // 更新游标位置
          currentCursorPosition.value = newTimestamp;
          const newCursorX = xScale(newTimestamp);

          // 更新游标屏幕位置比例
          if (chartContainer.value) {
            const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
            cursorScreenPositionRatio.value = (newCursorX - margin.left) / chartWidth;
          }

          // 更新游标显示
          updateCursorPosition(newCursorX, newTimestamp);
        }
      }
    }
  }
}

// 将焦点设置到DataViewer容器上，以便接收键盘事件
const setFocus = () => {
  if (dataViewerContainer.value) {
    dataViewerContainer.value.focus()
  }
}

// 相似性检索专用切换测量模式
const switchToMeasureMode = () => {
  if (interactionMode.value === 'measure') {
    return
  }
  interactionMode.value = 'measure'
  isMeasuring.value = true
  startMeasurement()
}

// 更新游标位置的方法（供外部组件调用）
const updateCursors = (cursors: { cursor1: number; cursor2: number }) => {
  console.log('DataViewer - updateCursors called with:', cursors);
  
  // 验证输入数据
  if (!cursors || typeof cursors.cursor1 !== 'number' || typeof cursors.cursor2 !== 'number') {
    console.warn('Invalid cursor data received:', cursors);
    return;
  }
  
  // 确保游标1小于游标2
  const cursor1 = Math.min(cursors.cursor1, cursors.cursor2);
  const cursor2 = Math.max(cursors.cursor1, cursors.cursor2);
  
  // 更新游标位置
  cursor1Position.value = cursor1;
  cursor2Position.value = cursor2;
  
  // 激活测量模式
  isMeasuring.value = true;
  activeCursor.value = 'cursor1';
  
  // 更新测量游标显示
  updateMeasurementCursors();
  
  // 重新计算测量数据
  updateMeasurementResults();
  
  console.log('Cursors updated successfully:', { cursor1, cursor2 });
};

watch(() => isMeasuring.value, (newVal) => {
  emit('update:isMeasuring', newVal)
}, { deep: true, immediate: true })

// 切换交互模式
const switchToMode = (mode: 'measure' | 'polygon' | null) => {
  // 如果当前已在该模式，且试图再次进入该模式，则退出到空模式
  if (interactionMode.value === mode && mode !== null) {
    interactionMode.value = null
    
    // 在退出测量模式时，结束测量
    if (mode === 'measure') {
      isMeasuring.value = false
      // 移除测量游标
      svg.selectAll('.cursor-line-1,.cursor-line-2').remove()
      svg.selectAll('.measurement-cursor-group').remove()
      svg.selectAll('.discrete-cursor-line').remove()
      
      // 恢复普通游标
      const x = xScale(currentCursorPosition.value);
      updateCursorPosition(x, currentCursorPosition.value);
      
      // 发送事件，清除测量数据
      emit('update:measurementData', undefined)
    } else if (mode === 'polygon') {
      // 清除多边形数据
      resetPolygon()
    }
    
    // 重置缩放类型
    zoomType.value = 'horizontal'
    
    return
  }
  
  // 在切换模式前，处理当前模式的退出
  if (interactionMode.value === 'measure') {
    isMeasuring.value = false
    // 移除测量游标
    svg.selectAll('.cursor-line-1,.cursor-line-2').remove()
    svg.selectAll('.measurement-cursor-group').remove()
    svg.selectAll('.discrete-cursor-line').remove()
    
    // 恢复普通游标
    const x = xScale(currentCursorPosition.value);
    updateCursorPosition(x, currentCursorPosition.value);
    
    // 发送事件，清除测量数据
    emit('update:measurementData', undefined)
  } else if (interactionMode.value === 'polygon') {
    // 清除多边形数据
    resetPolygon()
  }
  
  // 设置新模式
  interactionMode.value = mode
  
  // 根据新模式初始化
  if (mode === 'measure') {
    startMeasurement()
  } else if (mode === 'polygon') {
    // 清空多边形点集
    polygonPoints.value = []
    
    // 显示多边形绘制区域
    showPolygon.value = true
    
    // 隐藏数据抽取结果
    showPolygonData.value = false
  }
}

// 设置缩放类型
const setZoomType = (type: 'horizontal' | 'rect' | 'vertical') => {
  // 设置缩放类型
  zoomType.value = type
  console.log('缩放类型设置为:', type)
}

// 重置缩放，将x轴重置为全数据范围
const resetZoom = () => {
  isLoading.value = true
  // 在重置缩放前，保存当前游标的屏幕位置比例
  if (chartContainer.value) {
    const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
    
    // 保存普通游标的屏幕位置比例
    if (currentCursorPosition.value !== null) {
      const cursorX = xScale(currentCursorPosition.value);
      cursorScreenPositionRatio.value = (cursorX - margin.left) / chartWidth;
      console.log('重置缩放前保存游标屏幕位置比例:', cursorScreenPositionRatio.value);
    }
    
    // 保存测量游标的屏幕位置比例（如果在测量模式）
    if (interactionMode.value === 'measure' && isMeasuring.value) {
      cursor1ScreenRatio.value = (xScale(cursor1Position.value) - margin.left) / chartWidth;
      cursor2ScreenRatio.value = (xScale(cursor2Position.value) - margin.left) / chartWidth;
      console.log('重置缩放前保存测量游标屏幕位置比例:', cursor1ScreenRatio.value, cursor2ScreenRatio.value);
    }
  }
  
  // 获取数据更新defaultData
  let startTime = localStorage.getItem('startTime') || undefined
  let endTime = localStorage.getItem('endTime') || undefined
  const params = prepareApiParams(startTime, endTime, true)
  // 请求接口获取绘图数据
  getApiMethod()(params).then((res: any) => {
    if (res && res.code === 200) {
      let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
      Object.keys(res.data).forEach(key => {
        selectedParamsInfo.value.forEach((param) => {
          if (param.id === key) {
            resdataTemp[param.name] = res.data[key]
          }
        })
      })
      resdataTemp['RecTime'] = res.data['RecTime']
      defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
      timeIntervalList.value = []
      // 获取interval
      if (defaultData.value && defaultData.value.length > 0 ) {
        defaultData.value.forEach((item: any) => {
          timeIntervalList.value.push(item.interval)
        })
      }
      if (!defaultData.value || defaultData.value.length === 0) {
        isLoading.value = false
        return
      }


      console.log('重置缩放')

      // RJ模式：计算所有数据的时间范围
      let minTime: Date | null = null;
      let maxTime: Date | null = null;

      defaultData.value.forEach((channel: any) => {
        // 遍历所有选中的参数
        selectedParametersList.value?.forEach(paramName => {
          const paramData = channel.parameters[paramName];
          if (paramData && paramData.length > 0) {
            // 找到参数数据的时间范围
            const dataMinTime = new Date(paramData[0].timestamp);
            const dataMaxTime = new Date(paramData[paramData.length - 1].timestamp);
            
            if (!isNaN(dataMinTime.getTime()) && (!minTime || dataMinTime < minTime)) {
              minTime = dataMinTime;
            }
            if (!isNaN(dataMaxTime.getTime()) && (!maxTime || dataMaxTime > maxTime)) {
              maxTime = dataMaxTime;
            }
          }
        });
      });

      // 确保找到了有效的时间范围
      if (minTime && maxTime) {
        // 将时间精确到秒级，去除毫秒
        const roundedMinTime = new Date(Math.floor(minTime.getTime() / 1000) * 1000);
        const roundedMaxTime = new Date(Math.floor(maxTime.getTime() / 1000) * 1000);
        // 重置为完整时间范围
        xScale.domain([roundedMinTime, roundedMaxTime]);
        
        // 同时更新初始域，确保概览图手柄位置计算正确
        initialDomain.value = [roundedMinTime, roundedMaxTime];
      }
     
      // 检查是否成功设置了范围
      if (initialDomain.value[0] && initialDomain.value[1]) {
        
        // 重置矩形缩放标志
        isRectZoomActive.value = false;
        rectZoomYRanges.value = {};

        // 设置标记，指示需要忽略用户Y轴设置
        // skipUserYAxisSettings.value = true;
        
        // 使用用户设置的Y轴上下限
        
        // 更新坐标轴
        updateAxes();
        
        // 更新图表
        updateChart(true);

        if (chartContainer.value) {
          const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
          
          // 使用保存的屏幕位置比例计算新的游标位置
          const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
          const newTimestamp = xScale.invert(newCursorX);
          currentCursorPosition.value = newTimestamp;
          
          // 检查当前模式，决定更新哪种游标
          if (interactionMode.value === 'measure' && isMeasuring.value) {
            // 如果是测量模式，使用保存的屏幕位置比例计算新的测量游标位置
            const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
            const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
            
            cursor1Position.value = xScale.invert(cursor1X);
            cursor2Position.value = xScale.invert(cursor2X);
            
            // 确保更新测量游标
            updateMeasurementCursors();
            updateMeasurementResults();
          } else {
            // 普通模式下，更新普通游标
            updateCursorPosition(newCursorX, newTimestamp);
          }
        }
        
        // 重新绘制缩放带上的缩放窗口位置
        updateZoomWindowPosition();

        // 更新标注高亮
        updateAnnotationHighlights();

        // 短暂延时后恢复标记
        setTimeout(() => {
          skipUserYAxisSettings.value = false;
          isLoading.value = false
          zoomHistory.value = []
        }, 100);
      }
    } else {
      isLoading.value = false
    }
  }).catch((error: any) => {
    isLoading.value = false
    messageRef.value?.show({
      type: 'warning',
      content: error.response.data.msg,
      duration: 3000
    })
  });
};
const needUpdateFaultArea = ref(true) // 是否需要更新故障区域
// 回退缩放
const undoZoom = () => {
  needUpdateFaultArea.value = false
  // 检查缩放历史记录，实现回退
  if (zoomHistory.value.length > 1) {
    // 如果有多于2个历史记录，丢弃当前状态
    zoomHistory.value.pop();
    
    // 只保留1层历史：上次缩放状态
    while (zoomHistory.value.length > 1) {
      zoomHistory.value = [zoomHistory.value[zoomHistory.value.length - 1]];
    }
    
    // 获取撤销后的状态
    let prevState = zoomHistory.value[zoomHistory.value.length - 1];

    // 判断是否为矩形缩放模式
    if (zoomType.value === 'rect') {
      // 矩形缩放回退时也要重新获取defaultData
      const params = prepareApiParams(prevState.xDomain[0], prevState.xDomain[1])
      
      // 请求接口获取绘图数据
      getApiMethod()(params).then((res: any) => {
        if (res && res.code === 200) {
          // 更新数据
          let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
          Object.keys(res.data).forEach(key => {
            selectedParamsInfo.value.forEach((param) => {
              if (param.id === key) {
                resdataTemp[param.name] = res.data[key]
              }
            })
          })
          resdataTemp['RecTime'] = res.data['RecTime']
          defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
          timeIntervalList.value = []
          // 获取interval
          if (defaultData.value && defaultData.value.length > 0 ) {
            defaultData.value.forEach((item: any) => {
              timeIntervalList.value.push(item.interval)
            })
          }
          if (!defaultData.value || defaultData.value.length === 0) {
            isLoading.value = false
            return
          }
          // 应用历史状态中的X轴范围
          xScale.domain(prevState.xDomain);
          
          // 如果是矩形缩放模式，并且历史记录有Y轴范围信息，则也还原Y轴范围
          if (prevState.rectZoomYRanges) {
            rectZoomYRanges.value = prevState.rectZoomYRanges;
            isRectZoomActive.value = true;
          } else {
            // 重置矩形缩放标记
            isRectZoomActive.value = false;
            rectZoomYRanges.value = {};
          }
          
          // 更新坐标轴
          updateAxes();
          
          // 根据当前的缩放类型选择更新方式
          if (isRectZoomActive.value && rectZoomYRanges.value && Object.keys(rectZoomYRanges.value).length > 0) {
            // 矩形缩放模式下，使用保存的Y轴范围
            updateChartWithParamYScales();
          } else {
            // 其他情况使用普通更新
            updateChart(true);
          }
          
          // 确保游标线和游标点正确显示在相同的屏幕位置
          if (chartContainer.value) {
            const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
            const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
            const newTimestamp = xScale.invert(newCursorX);
            currentCursorPosition.value = newTimestamp;
            
            // 根据模式更新游标
            if (interactionMode.value === 'measure' && isMeasuring.value) {
              // 使用保存的屏幕位置比例计算新的游标位置
              const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
              const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
              
              cursor1Position.value = xScale.invert(cursor1X);
              cursor2Position.value = xScale.invert(cursor2X);
              
              // 更新测量游标和测量数据
              updateMeasurementCursors();
              updateMeasurementResults();
            } else {
              updateCursorPosition(newCursorX, newTimestamp);
            }
          }
          
          // 重新绘制缩放带上的缩放窗口位置
          updateZoomWindowPosition();

          // 更新标注高亮
          updateAnnotationHighlights();
        }
      })
      .catch((error: any) => {
        isLoading.value = false
        messageRef.value?.show({
          type: 'warning',
          content: error.response.data.msg,
          duration: 3000
        })
      })
    } else {
      // 水平缩放需要重新请求数据
      // 退回上一次缩放
      const params = prepareApiParams(prevState.xDomain[0], prevState.xDomain[1])
      
      // 请求接口获取绘图数据
      getApiMethod()(params).then((res: any) => {
        if (res && res.code === 200) {
          let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
          Object.keys(res.data).forEach(key => {
            selectedParamsInfo.value.forEach((param) => {
              if (param.id === key) {
                resdataTemp[param.name] = res.data[key]
              }
            })
          })
          resdataTemp['RecTime'] = res.data['RecTime']
          defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
          timeIntervalList.value = []
          // 获取interval
          if (defaultData.value && defaultData.value.length > 0 ) {
            defaultData.value.forEach((item: any) => {
              timeIntervalList.value.push(item.interval)
            })
          }
          if (!defaultData.value || defaultData.value.length === 0) {
            isLoading.value = false
            return
          }
          
          console.log('回退到上一次缩放')
          
          // 应用历史状态中的X轴范围
          xScale.domain(prevState.xDomain);
          
          if (zoomType.value === 'horizontal') {
            // 对于水平缩放，保存当前所有参数的y轴范围
            const selectedParams = selectedParametersList.value?.filter(param => 
              param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
            ) || [];
            
            // 记录每个参数的Y轴范围以便保持不变
            const paramYScaleRanges: Record<string, [number, number]> = {};
            selectedParams.forEach(paramName => {
              if (getParameterDisplayMode(paramName) === 'continuous') {
                const paramYScale = paramYScaleMap.value[paramName];
                if (paramYScale) {
                  // 获取当前Y轴范围并保存
                  paramYScaleRanges[paramName] = paramYScale.domain() as [number, number];
                }
              }
            });
            
            // 设置矩形缩放模式标记和保存Y轴范围
            isRectZoomActive.value = true;
            rectZoomYRanges.value = paramYScaleRanges;
          } else {
            // 重置矩形缩放标记
            isRectZoomActive.value = true;
            // 垂直缩放使用上一次的Y轴范围
            rectZoomYRanges.value = prevState.rectZoomYRanges;
          }
          
          // 更新坐标轴
          updateAxes();
          
          // 对于所有情况，使用保持Y轴范围的更新方法
          updateChartWithParamYScales();
          
          // 确保游标线和游标点正确显示在相同的屏幕位置
          if (chartContainer.value) {
            const chartWidth = chartContainer.value.clientWidth - margin.left - margin.right;
            const newCursorX = margin.left + (chartWidth * cursorScreenPositionRatio.value);
            const newTimestamp = xScale.invert(newCursorX);
            currentCursorPosition.value = newTimestamp;
            
            // 根据模式更新游标
            if (interactionMode.value === 'measure' && isMeasuring.value) {
              // 使用保存的屏幕位置比例计算新的游标位置
              const cursor1X = margin.left + (chartWidth * cursor1ScreenRatio.value);
              const cursor2X = margin.left + (chartWidth * cursor2ScreenRatio.value);
              
              cursor1Position.value = xScale.invert(cursor1X);
              cursor2Position.value = xScale.invert(cursor2X);
              
              // 更新测量游标和测量数据
              updateMeasurementCursors();
              updateMeasurementResults();
            } else {
              updateCursorPosition(newCursorX, newTimestamp);
            }
          }
          
          // 重新绘制缩放带上的缩放窗口位置
          updateZoomWindowPosition();

          // 更新标注高亮
          updateAnnotationHighlights();

          isLoading.value = false
        } else {
          isLoading.value = false
        }
      })
      .catch((error: any) => {
        isLoading.value = false
        messageRef.value?.show({
          type: 'warning',
          content: error.response.data.msg,
          duration: 3000
        })
      })
    }
  } else if (zoomHistory.value.length == 1) {
    // 第二次回退（或只有一次缩放），直接回到初始状态
    resetZoom();
    zoomHistory.value = []
  } else {
    // 没有缩放或已经是初始状态
    isLoading.value = false
    return;
  }
}

// 更新临时连接线，添加裁剪
const updateTempLine = (x: number, y: number) => {
  if (!tempLine.value || polygonPoints.value.length === 0) return
  
  const lastPoint = polygonPoints.value[polygonPoints.value.length - 1]
  
  // 确保临时线在裁剪组内
  if (!svg.select('.temp-line-group').empty()) {
    tempLine.value
      .attr('x1', lastPoint.x)
      .attr('y1', lastPoint.y)
      .attr('x2', x)
      .attr('y2', y)
  } else {
    // 如果不存在裁剪组，创建一个新的
    svg.selectAll('.temp-line').remove()
    
    const tempLineGroup = svg.append('g')
      .attr('class', 'temp-line-group')
      .attr('clip-path', 'url(#chart-area)')
    
    tempLine.value = tempLineGroup.append('line')
      .attr('class', 'temp-line')
      .attr('x1', lastPoint.x)
      .attr('y1', lastPoint.y)
      .attr('x2', x)
      .attr('y2', y)
      .attr('stroke', 'blue')
      .attr('stroke-width', 1)
      .attr('stroke-dasharray', '3,3')
  }
}

// 添加点到多边形
const addPointToPolygon = (x: number, y: number) => {
  // 确保点在有效区域内
  const width = chartContainer.value!.clientWidth
  const height = chartContainer.value!.clientHeight
  
  if (x < margin.left || x > width - margin.right || 
      y < margin.top || y > height - margin.bottom) {
    return
  }
  
  // 转换为数据坐标
  const dataX = xScale.invert(x)
  const dataY = yScale.invert(y)
  
  // 检查是否点击了第一个点以完成多边形
  if (polygonPoints.value.length >= 3) {
    const firstPoint = polygonPoints.value[0]
    // 计算点击位置与第一个点之间的距离
    const distance = Math.sqrt(Math.pow(x - firstPoint.x, 2) + Math.pow(y - firstPoint.y, 2))
    
    // 如果距离小于一定阈值(10像素)，则认为用户点击了第一个点
    if (distance < 10) {
      // 完成多边形
      completePolygon()
      return
    }
  }
  
  // 添加点
  polygonPoints.value.push({ x, y, dataX, dataY })
  
  // 更新多边形显示
  updatePolygonDisplay()
  
  // 如果是第一个点，初始化临时线
  if (polygonPoints.value.length === 1) {
    // 确保svg存在
    if (!svg) return
    
    // 移除任何已存在的临时线
    svg.selectAll('.temp-line').remove()
    svg.selectAll('.temp-line-group').remove()
    
    // 创建临时线的裁剪组
    const tempLineGroup = svg.append('g')
      .attr('class', 'temp-line-group')
      .attr('clip-path', 'url(#chart-area)')
    
    tempLine.value = tempLineGroup.append('line')
      .attr('class', 'temp-line')
      .attr('x1', x)
      .attr('y1', y)
      .attr('x2', x)
      .attr('y2', y)
      .attr('stroke', 'blue')
      .attr('stroke-width', 1)
      .attr('stroke-dasharray', '3,3')
  }
}

// 更新多边形显示，添加裁剪
const updatePolygonDisplay = () => {
  if (polygonPoints.value.length < 2) return
  
  // 移除现有多边形和点
  if (currentPolygon.value) {
    currentPolygon.value.remove()
  }
  svg.selectAll('.polygon-point').remove()
  svg.selectAll('.polygon-group').remove()
  
  // 创建多边形组并应用裁剪
  const polygonGroup = svg.append('g')
    .attr('class', 'polygon-group')
    .attr('clip-path', 'url(#chart-area)')
  
  // 生成多边形点字符串
  const pointsString = polygonPoints.value
    .map(point => `${point.x},${point.y}`)
    .join(' ')
  
  // 绘制新多边形
  currentPolygon.value = polygonGroup.append('polygon')
    .attr('class', 'polygon-selection')
    .attr('points', pointsString)
    .attr('fill', 'rgba(173, 216, 230, 0.3)')
    .attr('stroke', 'blue')
    .attr('stroke-width', 1)
    .style('pointer-events', 'fill') // 改为fill以允许点击多边形区域
  
  // 添加顶点标记
  polygonPoints.value.forEach((point, index) => {
    const circle = polygonGroup.append('circle')
      .attr('class', 'polygon-point')
      .attr('cx', point.x)
      .attr('cy', point.y)
      .attr('r', 6) // 增大半径以便于点击
      .attr('fill', index === 0 ? 'red' : 'blue') // 第一个点设为红色以区分
      .attr('stroke', 'white')
      .attr('stroke-width', 1.5)
      .style('pointer-events', 'all') // 确保点可点击
      
    // 添加点击事件处理
    if (index === 0 && polygonPoints.value.length >= 3) {
      circle.on('click', () => {
        completePolygon()
      })
    }
      
    circle.append('title')
      .text(`点 ${index + 1}: (${point.dataX.toFixed(3)}, ${point.dataY.toFixed(3)})${index === 0 ? ' - 点击此点完成多边形' : ''}`)
  })
}

// 完成多边形
const completePolygon = () => {
  // 移除临时线
  if (tempLine.value) {
    tempLine.value.remove()
    tempLine.value = null
  }
  
  // 设置多边形为显示状态
  showPolygon.value = true
  
  // 显示多边形数据抽屉
  showPolygonData.value = true
}

// 清除多边形，确保移除所有相关元素
const clearPolygon = () => {
  // 清除所有点
  polygonPoints.value = []
  
  // 移除多边形和点标记
  if (currentPolygon.value) {
    currentPolygon.value.remove()
    currentPolygon.value = null
  }
  
  svg.selectAll('.polygon-point').remove()
  svg.selectAll('.polygon-group').remove()
  
  // 移除临时线
  if (tempLine.value) {
    tempLine.value.remove()
    tempLine.value = null
  }
  svg.selectAll('.temp-line-group').remove()
  
  // 重置状态
  showPolygon.value = false
  showPolygonData.value = false
}

// 复制多边形数据
const copyPolygonData = () => {
  const data = polygonPoints.value
    .map((point, index) => `${index + 1},${point.dataX.toFixed(3)},${point.dataY.toFixed(3)}`)
    .join('\n')
  
  const header = '序号,X轴(时间),Y轴(数值)\n'
  const textToCopy = header + data
  
  navigator.clipboard.writeText(textToCopy)
    .then(() => {
      alert('数据已复制到剪贴板')
    })
    .catch(err => {
      console.error('复制失败:', err)
      alert('复制失败，请手动选择并复制')
    })
}

// 更新坐标轴和网格线
const updateAxes = () => {
  if (!svg || !chartContainer.value) return
  
  const width = chartContainer.value.clientWidth
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 更新坐标轴
  svg.selectAll('.axis').remove()
  
  // 添加X轴
  svg.append('g')
    .attr('class', 'axis x-axis')
    .attr('transform', `translate(0,${height - margin.bottom})`)
    .call(d3.axisBottom(xScale as d3.ScaleTime<number, number>).tickFormat(d3.timeFormat('%Y-%m-%d %H:%M:%S')).ticks(5))

  // 移除网格线更新代码
  svg.selectAll('.grid').remove()
}

// 修改剪裁区域函数，考虑离散量区域和缩放带
const updateClipPath = (width: number, height: number) => {
  // 检查主图表裁剪区域是否存在
  const clipPath = svg.select('#chart-area')
  if (clipPath.empty()) {
    // 如果不存在，创建新的裁剪区域
    svg.append('defs')
      .append('clipPath')
      .attr('id', 'chart-area')
      .append('rect')
      .attr('x', margin.left)
      .attr('y', margin.top)
      .attr('width', width - margin.left - margin.right)
      .attr('height', height - margin.top - margin.bottom) // 只考虑主图表区域
  } else {
    // 如果存在，更新尺寸
    clipPath.select('rect')
      .attr('width', width - margin.left - margin.right)
      .attr('height', height - margin.top - margin.bottom) // 只考虑主图表区域
  }
  
  // 获取缩放带的Y位置
  const zoomBarY = getZoomBarY()
  
  // 检查缩放带裁剪区域是否存在
  const zoomClipPath = svg.select('#zoom-bar-area')
  if (zoomClipPath.empty()) {
    // 如果不存在，创建新的裁剪区域
    svg.append('defs')
      .append('clipPath')
      .attr('id', 'zoom-bar-area')
      .append('rect')
      .attr('x', margin.left)
      .attr('y', zoomBarY) // 使用计算的位置
      .attr('width', width - margin.left - margin.right)
      .attr('height', zoomBarHeight.value - 10) // 缩放带高度
  } else {
    // 如果存在，更新尺寸和位置
    zoomClipPath.select('rect')
      .attr('x', margin.left)
      .attr('y', zoomBarY) // 使用计算的位置
      .attr('width', width - margin.left - margin.right)
      .attr('height', zoomBarHeight.value - 10) // 缩放带高度
  }
}

// 修改离散量区域显示函数
const updateDiscreteArea = () => {
  if (!svg || !chartContainer.value || !xScale) return

  const width = chartContainer.value.clientWidth
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 移除可能存在的滚动容器
  const existingScrollContainer = document.querySelector('.discrete-scroll-container')
  if (existingScrollContainer && existingScrollContainer.parentNode) {
    // 在移除前先清除内部SVG中的游标线
    const innerSvg = existingScrollContainer.querySelector('.discrete-inner-svg')
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove()
    }
    
    existingScrollContainer.parentNode.removeChild(existingScrollContainer)
  }
  
  // 彻底清除所有与离散区域相关的元素
  svg.selectAll('.discrete-band').remove()
  svg.selectAll('.discrete-label').remove()
  svg.selectAll('.discrete-background').remove()
  svg.selectAll('.param-label-overlay').remove()
  svg.selectAll('.channel-param-label').remove()
  svg.selectAll('.discrete-container').remove()
  svg.selectAll('.discrete-cursor-line').remove() // 也清除主SVG中的游标线
  
  // 确保离散区域组存在
  let discreteGroup: d3.Selection<any, unknown, null, undefined> = svg.select('.discrete-group')
  if (discreteGroup.empty()) {
    discreteGroup = svg.append('g')
      .attr('class', 'discrete-group')
      .attr('transform', `translate(0, ${height})`)
  } else {
    // 如果存在，清空其中所有内容
    discreteGroup.selectAll('*').remove()
  }
  
  // 如果没有选中的参数，则不需要继续渲染
  if (!selectedParametersList.value || selectedParametersList.value.length === 0) {
    return
  }
  // 初始没有离散参数时，确保离散区域高度为0
  // 不直接修改计算属性的值
  
  // 创建离散量区域的背景
  discreteGroup.append('rect')
    .attr('class', 'discrete-background')
    .attr('x', margin.left)
    .attr('y', 0 + faultBandsAreaHeight.value)
    .attr('width', width - margin.left - margin.right)
    .attr('height', discreteAreaHeight.value)
    .attr('fill', isDarkMode.value ? '#1e1e1e' : '#f5f5f5')
    .attr('stroke', isDarkMode.value ? '#444444' : '#e0e0e0')
    .attr('stroke-width', 1)
  
  // 查找所有应该以离散形式显示的参数(discreteParams)
  // 遍历所有通道中的参数，找出设置为离散显示的
  const discreteParams = new Set<string>()
  
  // 确保只处理当前选中的参数
  // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || []
  
  // 检查是否有任何离散参数被选中
  let hasAnyDiscreteParam = false
  
  defaultData.value.forEach(channel => {
    const channelId = channel.channelId
    
    // 只遍历当前选中的参数
    selectedParams.forEach(paramName => {
      // 检查该参数是否在当前通道的数据中
      if (!channel.parameters[paramName]) return
      
      // 获取此参数在此通道的显示方式
      const displayMode = getChannelSettingDisplayMode(paramName, channelId)
      // 如果该通道设置为离散显示且通道可见，收集此参数
      if (displayMode === 'discrete' && getChannelSettingVisible(paramName, channelId)) {
        discreteParams.add(paramName)
        hasAnyDiscreteParam = true
      }
    })
  })
  
  // 如果没有离散参数，则退出
  if (discreteParams.size === 0 || !hasAnyDiscreteParam) {    
    // 更新X轴位置
    const mainChartHeight = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
    svg.select('.x-axis')
      .attr('transform', `translate(0,${mainChartHeight - margin.bottom})`)
    
    return
  }
  
  // 从defaultData.value中提取离散参数数据
  const discreteData: Array<{ paramId: string; paramName: string; data: Array<{
    channelId: string;
    values: Array<{ timestamp: number | string; value: number }>;
  }> }> = []
  
  // 为每个离散参数收集数据
  discreteParams.forEach(paramName => {
    const paramData: Array<{
      channelId: string;
      values: Array<{ timestamp: number | string; value: number }>;
    }> = []
    
    // 从所有通道收集此参数的数据
    defaultData.value.forEach(channel => {
      const channelId = channel.channelId
      const data = channel.parameters[paramName]
      
      // 检查此参数在此通道的显示方式和可见性
      const displayMode = getChannelSettingDisplayMode(paramName, channelId)
      const isVisible = getChannelSettingVisible(paramName, channelId)
      
      // 只收集设置为离散显示且可见的通道数据
      if (displayMode === 'discrete' && isVisible && data && Array.isArray(data)) {
        paramData.push({
          channelId,
          values: data
        })
      }
    })
    
    // 如果有此参数的数据，添加到discreteData
    if (paramData.length > 0) {
      discreteData.push({
        paramId: paramName,  // 添加paramId属性
        paramName: paramName,
        data: paramData
      })
    }
  })
  
  // 离散参数总数
  const paramCount = discreteData.length
  
  // 每个通道的高度
  const channelHeight = 20 // 固定高度，可以根据需要调整
  
  // 参数间的间距
  const paramSpacing = 0
  
  // 计算总的垂直空间需求
  let currentY = 0 + faultBandsAreaHeight.value // 起始位置留出5px间距
  let paramIndex = 0
  
  // 计算离散参数和通道总数
  let totalDiscreteChannels = 0
  let discreteParamCount = discreteData.length
  
  // 计算总的通道数量
  discreteData.forEach(param => {
    totalDiscreteChannels += param.data.length
  })
  
  // 计算实际内容高度
  const calculatedHeight = baseChannelHeight * totalDiscreteChannels + (discreteParamCount - 1) * paramSpacing
  const needsScroll = calculatedHeight > maxDiscreteAreaHeight
  
  // 用于存储滚动容器引用
  let discreteScrollContainer: HTMLElement | null = null
  
  // 如果内容高度超过最大高度，创建一个可滚动的外部容器
  if (needsScroll) {
    // 创建一个外部的HTML容器用于滚动
    discreteScrollContainer = document.createElement('div')
    discreteScrollContainer.className = 'discrete-scroll-container'
    discreteScrollContainer.style.position = 'absolute'
    discreteScrollContainer.style.top = `${height + faultBandsAreaHeight.value}px`
    discreteScrollContainer.style.left = `0px`
    discreteScrollContainer.style.width = `${width}px`
    discreteScrollContainer.style.height = `${discreteAreaHeight.value}px`
    discreteScrollContainer.style.overflowY = 'auto'
    discreteScrollContainer.style.overflowX = 'hidden'
    discreteScrollContainer.style.zIndex = '10'
    
    currentY = 0
    
    // 创建内部SVG元素
    const innerSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
    innerSvg.setAttribute('width', `${width}px`)
    innerSvg.setAttribute('height', `${calculatedHeight}px`)
    innerSvg.classList.add('discrete-inner-svg')
    
    // 将SVG添加到容器中
    discreteScrollContainer.appendChild(innerSvg)
    
    // 将容器添加到DOM中
    chartContainer.value.appendChild(discreteScrollContainer)
    
    // 使用D3选择新创建的SVG
    const innerSvgSelection = d3.select(innerSvg)
    
    // 创建背景
    innerSvgSelection.append('rect')
      .attr('x', margin.left)
      .attr('y', 0)
      .attr('width', width - margin.left - margin.right)
      .attr('height', calculatedHeight)
      .attr('fill', isDarkMode.value ? '#1e1e1e' : '#f5f5f5')
      .attr('stroke', isDarkMode.value ? '#444444' : '#e0e0e0')
      .attr('stroke-width', 1)
    
    // 在下面的代码中使用innerSvgSelection替代discreteGroup
    discreteGroup = innerSvgSelection
  }
  
  // 为每个参数创建色带
  discreteData.forEach(param => {
    const { paramName, data } = param
    
    // 计算此参数有多少个通道
    const channelCount = data.length
    
    // 为每个通道创建色带
    let channelIndex = 0
    
    data.forEach(channelData => {
      const { channelId, values } = channelData
      
      // 获取当前X轴范围内的数据
      const xDomain = xScale.domain()
      let xDomainStart: number
      let xDomainEnd: number
      xDomainStart = Math.floor((xDomain[0] as Date).getTime() / 1000) * 1000
      xDomainEnd = Math.floor((xDomain[1] as Date).getTime() / 1000) * 1000

      const visibleData = values.filter(d => 
        new Date(d.timestamp as string).getTime() >= xDomainStart && new Date(d.timestamp as string).getTime() <= xDomainEnd
      )
      
      if (visibleData.length === 0) {
        channelIndex++
        return // 跳过没有可见数据的通道
      }
      
      // 对数据进行分段处理，找出连续的相同值区间
      const segments: Array<{start: number | Date, end: number | Date, value: number}> = []
      let currentSegment = {
        start: visibleData[0].timestamp,
        end: visibleData[0].timestamp,
        value: visibleData[0].value
      }
      
      for (let i = 1; i < visibleData.length; i++) {
        const point = visibleData[i]
        
        if (point.value === currentSegment.value) {
          // 继续当前段
          currentSegment.end = point.timestamp
        } else {
          // 结束当前段，开始新段
          segments.push({...currentSegment})
          currentSegment = {
            start: point.timestamp,
            end: point.timestamp,
            value: point.value
          }
        }
      }
      
      // 添加最后一个段
      segments.push({...currentSegment})
      
      // 在RJ模式下，扩展最后一个段到x轴右边界
      if (segments.length > 0) {
        const lastSegment = segments[segments.length - 1];
        const xDomain = xScale.domain();
        const maxTime = xDomain[1] as Date;
        const lastSegmentEnd = new Date(lastSegment.end as string);
        
        // 如果最后一个段的结束时间早于x轴右边界，延伸到右边界之后
        const extendTime = new Date(Math.max(maxTime.getTime(), lastSegmentEnd.getTime() + 1000));
        // 加上8小时的时区偏移（中国时区UTC+8）
        const extendTimeWithOffset = new Date(extendTime.getTime() + 8 * 60 * 60 * 1000);
        
        // 更新最后一个段的结束时间
        segments[segments.length - 1] = {
          ...lastSegment,
          end: extendTimeWithOffset.toISOString().replace('T', ' ').replace('Z', '').substring(0, 19)
        };
      }
      
      // 计算当前通道的Y坐标
      const channelY = currentY
      // 为每个分段创建色带，按照时间顺序依次渲染
      segments.forEach((segment, index) => {
        // 在RJ模式下，需要将时间戳转换为Date对象
        let segmentStart: number | Date;
        let segmentEnd: number | Date;
        
        segmentStart = new Date(segment.start as string);
        segmentEnd = new Date(segment.end as string);
        
        const x1 = Math.max(margin.left, xScale(segmentStart))
        const x2 = Math.min(width - margin.right, xScale(segmentEnd))
        
        // 确保与下一个段无缝连接
        let adjustedX2 = x2;
        if (index < segments.length - 1) {
          const nextSegment = segments[index + 1];
          let nextSegmentStart: number | Date;
          
          nextSegmentStart = new Date(nextSegment.start as string);
          
          const nextX1 = Math.max(margin.left, xScale(nextSegmentStart));
          // 如果有间隙，延伸当前段到下一段的起点
          if (nextX1 > x2) {
            adjustedX2 = nextX1;
          }
        }
        
        const bandWidth = Math.max(0, adjustedX2 - x1) // 确保宽度不为负
        
        if (bandWidth > 0) {
          const baseColor = segment.value == 0 ? '#09bb07' : (isDarkMode.value ? '#ff00ff':'#ed0902') // 0为绿色，1为红色
          // const baseColor = segment.value == 0 ? '#09bb07' : '#ed0902' // 0为绿色，1为红色

          // 创建色带
          discreteGroup.append('rect')
            .attr('class', 'discrete-band')
            .attr('x', x1)
            .attr('y', channelY)
            .attr('width', bandWidth)
            .attr('height', channelHeight)
            .attr('fill', baseColor)
            .attr('opacity', 0.8)
            .append('title')
            .text(`${paramName} - ${channelId}: ${segment.value == 0 ? '正常' : '告警'} (${segment.start + ' - ' + segment.end})`)
        }
      })
      
      // 添加参数名文本
      discreteGroup.append('text')
        .attr('class', 'channel-param-label')
        .attr('x', margin.left + 5)
        .attr('y', channelY + channelHeight / 2)
        .attr('text-anchor', 'start')
        .attr('dominant-baseline', 'middle')
        .attr('font-size', '11px')
        .attr('font-weight', 'bold')
        .attr('fill', '#fff') // 使用白色
        .text(`${paramName}`);
      
      // 增加通道索引和当前Y位置
      channelIndex++;
      currentY += channelHeight;
    })
    
    // 如果不是最后一个参数，添加参数间间距
    if (paramIndex < paramCount - 1) {
      currentY += paramSpacing;
    }
    
    paramIndex++;
  })
  
  // 在函数结尾添加
  
  // 更新缩放带位置，确保其位置正确
  nextTick(() => {
    // 更新X轴位置
    const mainChartHeight = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
    svg.select('.x-axis')
      .attr('transform', `translate(0,${mainChartHeight - margin.bottom})`)
    
    // 验证离散区域是否应该可见，如果不应该可见，确保其隐藏
    if (discreteAreaHeight.value === 0 || discreteParams.size === 0) {
      svg.select('.discrete-group').style('visibility', 'hidden')
      svg.select('.discrete-background').style('visibility', 'hidden')
    } else {
      svg.select('.discrete-group').style('visibility', 'visible')
      svg.select('.discrete-background').style('visibility', 'visible')
    }
  })
}

const getChannelName = (channel: string) => {
  if (channel == 'leftA') return '左发A'
  if (channel == 'leftB') return '左发B'
  if (channel == 'rightA') return '右发A'
  if (channel == 'rightB') return '右发B'
  if (channel == 'RJ') return '燃机'
}
// 增加添加游标线到离散量区域的函数
const addCursorToDiscreteArea = (timestamp: number | Date) => {
  if (!svg || !chartContainer.value || !xScale) return
  
  // 先移除任何现有的游标线
  svg.selectAll('.discrete-cursor-line').remove()
  
  // 检查是否有可滚动的离散区域
  const discreteScrollContainer = document.querySelector('.discrete-scroll-container')
  
  if (discreteScrollContainer) {
    // 在可滚动容器内添加游标线
    const innerSvg = discreteScrollContainer.querySelector('.discrete-inner-svg')
    if (innerSvg) {
      // 先移除内部SVG中可能存在的旧游标线
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove()
      
      const innerSvgSelection = d3.select(innerSvg)
      
      // 计算x坐标
      const x = xScale(timestamp)
      
      // 在内部SVG中添加垂直游标线
      innerSvgSelection.append('line')
        .attr('class', 'discrete-cursor-line')
        .attr('x1', x)
        .attr('y1', 0)
        .attr('x2', x)
        .attr('y2', innerSvg.getAttribute('height'))
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 1)
    }
  } else {
    // 使用常规方式添加游标线
    // 获取离散区域组
    const discreteGroup = svg.select('.discrete-group')
    if (discreteGroup.empty()) return
    
    // 计算x坐标
    const x = xScale(timestamp)
    
    // 添加垂直游标线
    discreteGroup.append('line')
      .attr('class', 'discrete-cursor-line')
      .attr('x1', x)
      .attr('y1', 0 + faultBandsAreaHeight.value)
      .attr('x2', x)
      .attr('y2', discreteAreaHeight.value + faultBandsAreaHeight.value)
      .attr('stroke', '#1890ff')
      .attr('stroke-width', 1)
  }
}

// 增加添加游标线到故障量区域的函数
const addCursorToFaultArea = (timestamp: number | Date) => {
  if (!svg || !chartContainer.value || !xScale) return
  
  // 先移除任何现有的游标线
  svg.selectAll('.fault-cursor-line').remove()

  // 检查是否有可滚动的故障量区域
  const faultScrollContainer = document.querySelector('.fault-scroll-container')
  if (faultScrollContainer) {
    // 在可滚动容器内添加游标线
    const innerSvg = faultScrollContainer.querySelector('.fault-inner-svg')
    if (innerSvg) {
      // 先移除内部SVG中可能存在的旧游标线
      d3.select(innerSvg).selectAll('.fault-cursor-line').remove()
      
      const innerSvgSelection = d3.select(innerSvg)
      
      // 计算x坐标
      const x = xScale(timestamp)
      
      // 获取内部SVG的实际高度
      const svgHeight = parseFloat(innerSvg.getAttribute('height') || '0')
      // 添加垂直游标线
      innerSvgSelection.append('line')
        .attr('class', 'fault-cursor-line')
        .attr('x1', x)
        .attr('y1', 0)
        .attr('x2', x)
        .attr('y2', svgHeight)
        .attr('stroke', '#1890ff')
        .attr('stroke-width', 1)
        .style('pointer-events', 'none')
    }
  } else {
    // 使用常规方式添加游标线到主SVG
    // 检查是否有故障带区域
    const faultBandGroup = svg.select('.fault-band')
    if (faultBandGroup.empty()) return
    
    // 计算故障带区域的位置
    const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
    const faultBandStartY = height
    
    // 计算x坐标
    const x = xScale(timestamp)
    
    // 在主SVG中添加垂直游标线
    svg.append('line')
      .attr('class', 'fault-cursor-line')
      .attr('x1', x)
      .attr('y1', faultBandStartY)
      .attr('x2', x)
      .attr('y2', faultBandStartY + faultBandsAreaHeight.value - 10)
      .attr('stroke', '#1890ff')
      .attr('stroke-width', 1)
      .style('pointer-events', 'none')
  }
}


// 创建一个新函数，专门用于请求概览图数据
const queryOverviewData = (params: any) => {
  // 使用prepareApiParams来准备概览图参数（获取完整数据）

  const overviewParams = prepareApiParams(startTimeLocal.value, endTimeLocal.value, true)
  
  // 请求概览图数据
  return getApiMethod()(overviewParams).then((res: any) => {
    if (res && res.code === 200) {
      let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
      Object.keys(res.data).forEach(key => {
        selectedParamsInfo.value.forEach((param) => {
          if (param.id === key) {
            resdataTemp[param.name] = res.data[key]
          }
        })
      })
      resdataTemp['RecTime'] = res.data['RecTime']
      outViewData.value = [{ channelId: "RJ", parameters: resdataTemp }];
      return outViewData.value;
    }
    return null;
  }).catch((error: any) => {
    console.error('获取概览图数据出错:', error);
    messageRef.value?.show({
      type: 'warning',
      content: error.response.data.msg,
      duration: 3000
    })
    return null;
  });
};

// 修改查询详细图表数据的函数
const queryChartData = (params: any) => {
  // 开始数据加载
  isLoading.value = true
  // 向父组件发送loading状态
  emit('update:loading', true)
  
  return getApiMethod()(params).then((res: any) => {
    if (res && res.code === 200) {
      let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
      Object.keys(res.data).forEach(key => {
        selectedParamsInfo.value.forEach((param) => {
          if (param.id === key) {
            resdataTemp[param.name] = res.data[key]
          }
        })
      })
      resdataTemp['RecTime'] = res.data['RecTime']
      defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
      timeIntervalList.value = []
      // 获取interval
      if (defaultData.value && defaultData.value.length > 0 ) {
        defaultData.value.forEach((item: any) => {
          timeIntervalList.value.push(item.interval)
        })
      }
      if (zoomHistory.value.length === 0) {
        outViewData.value = [{ channelId: "RJ", parameters: resdataTemp }];
        const zoomBarGroup = svg.select('.zoom-bar-group')
        if (!zoomBarGroup.empty() && chartContainer.value) {
          try {
            const width = chartContainer.value.clientWidth
            const height = chartContainer.value.clientHeight
            // 在缩放带上显示整体数据概览
            updateOverviewChart(zoomBarGroup as d3.Selection<SVGGElement, unknown, null, undefined>, width, height)
          } catch (error) {
            console.error('更新缩放带概览图出错:', error)
          }
        }
      }
      return defaultData.value;
    }
    return null;
  }).catch(error => {
    console.error('获取图表数据出错:', error);
    // let resdata = {
    //   RecTime: [
    //     { timestamp: "2025-11-12 00:11:11", value: 1752279071000 },
    //     { timestamp: "2025-11-12 00:11:12", value: 1752279072000 },
    //     { timestamp: "2025-11-12 00:11:13", value: 1752279073000 },
    //     { timestamp: "2025-11-12 00:11:14", value: 1752279074000 },
    //     { timestamp: "2025-11-12 00:11:15", value: 1752279075000 },
    //     { timestamp: "2025-11-12 00:11:16", value: 1752279076000 },
    //     { timestamp: "2025-11-12 00:11:17", value: 1752279077000 },
    //     { timestamp: "2025-11-12 00:11:18", value: 1752279078000 },
    //     { timestamp: "2025-11-12 00:11:19", value: 1752279079000 },
    //     { timestamp: "2025-11-12 00:11:20", value: 1752279080000 },
    //   ],
    //   P5: [
    //     { timestamp: "2025-11-12 00:11:11", value: 0 },
    //     { timestamp: "2025-11-12 00:11:12", value: 1 },
    //     { timestamp: "2025-11-12 00:11:13", value: 1 },
    //     { timestamp: "2025-11-12 00:11:14", value: 1 },
    //     { timestamp: "2025-11-12 00:11:15", value: 0 },
    //     { timestamp: "2025-11-12 00:11:16", value: 1 },
    //     { timestamp: "2025-11-12 00:11:17", value: 0 },
    //     { timestamp: "2025-11-12 00:11:18", value: 1 },
    //     { timestamp: "2025-11-12 00:11:19", value: 0 },
    //     { timestamp: "2025-11-12 00:11:20", value: 1 },
    //   ],
    //   P4: [
    //     { timestamp: "2025-11-12 00:11:11", value: 100 },
    //     { timestamp: "2025-11-12 00:11:12", value: 101 },
    //     { timestamp: "2025-11-12 00:11:13", value: 105 },
    //     { timestamp: "2025-11-12 00:11:14", value: 101 },
    //     { timestamp: "2025-11-12 00:11:15", value: 100 },
    //     { timestamp: "2025-11-12 00:11:16", value: 101 },
    //     { timestamp: "2025-11-12 00:11:17", value: 100 },
    //     { timestamp: "2025-11-12 00:11:18", value: 107 },
    //     { timestamp: "2025-11-12 00:11:19", value: 108 },
    //     { timestamp: "2025-11-12 00:11:20", value: 107 },
    //   ],
    // };
    // let resdataTemp: Record<string, Array<{ timestamp: string, value: number }>> = {};
    // Object.keys(resdata).forEach(key => {
    //   selectedParamsInfo.value.forEach((param) => {
    //     if (param.id === key) {
    //       resdataTemp[param.name] = resdata[key]
    //     }
    //   })
    // })
    // resdataTemp['RecTime'] = resdata['RecTime']
    // defaultData.value = [{ channelId: "RJ", parameters: resdataTemp }];
    // outViewData.value = [{ channelId: "RJ", parameters: resdataTemp }];
    // let res = defaultData.value;
    // isLoading.value = false;
    // // 向父组件发送loading结束状态
    // emit("update:loading", false);
    // return res;
    emit("handleDataError", "warning", error.response.data.msg);
    return null;
  }).finally(() => {
    // 数据加载完成后延迟一点时间再关闭loading
    setTimeout(() => {
      isLoading.value = false
      // 向父组件发送loading结束状态
      emit('update:loading', false)
    }, 100)
  });
};

// 添加一个函数用于处理新选择的参数，同步到当前的Y轴缩放范围
const syncNewParametersToCurrentYAxisRange = (newSelectedParams: string[] | undefined, oldSelectedParams: string[] | undefined) => {
  
  if (!newSelectedParams || !oldSelectedParams || newSelectedParams.length <= oldSelectedParams.length) {
    return; // 没有新增参数，不需要处理
  }

  // 查找新添加的参数
  const addedParams = newSelectedParams.filter(p => !oldSelectedParams.includes(p));
  
  if (addedParams.length === 0) {
    return;
  }

  // 如果在缩放模式下
  if (zoomType.value === 'vertical' || zoomType.value === 'rect') {
    
    if (isRectZoomActive.value && rectZoomYRanges.value && Object.keys(rectZoomYRanges.value).length > 0) {
      // 获取当前所有参数的平均Y轴范围
      let avgMin = 0;
      let avgMax = 0;
      let count = 0;

      // 计算现有参数的平均Y轴范围
      Object.entries(rectZoomYRanges.value).forEach(([paramName, range]) => {
        if (oldSelectedParams.includes(paramName)) {
          avgMin += range[0];
          avgMax += range[1];
          count++;
        }
      });

      if (count > 0) {
        avgMin /= count;
        avgMax /= count;
        
        console.log(`为新参数设置Y轴范围: [${avgMin}, ${avgMax}]`);

        // 为新增的参数设置相同的Y轴范围
        addedParams.forEach(paramName => {
          if (getParameterDisplayMode(paramName) !== 'continuous') {
            return; // 跳过离散参数
          }
          rectZoomYRanges.value[paramName] = [avgMin, avgMax];
        });
      } else {
        console.log('没有找到现有参数的Y轴范围数据');
      }
    } else {
      console.log('没有活动的矩形缩放或Y轴范围数据');
    }
  } else {
    // 非缩放模式下，确保新参数有适当的Y轴比例
    addedParams.forEach(paramName => {
      if (getParameterDisplayMode(paramName) === 'continuous') {
        // 为新参数设置适当的Y轴比例
        // 这里可以添加获取参数默认范围的逻辑
      }
    });
  }
}

// 在watch函数中调用此函数
watch(() => props.selectedParameters, (newSelectedParams, oldSelectedParams) => {  
  // 处理新增参数的Y轴范围
  syncNewParametersToCurrentYAxisRange(newSelectedParams, oldSelectedParams);
  isChangeTask.value = props.changeTask
  // 清理不再使用的参数的Y轴比例尺
  if (oldSelectedParams && newSelectedParams) {
    const removedParams = oldSelectedParams.filter(p => !newSelectedParams.includes(p));
    removedParams.forEach(paramName => {
      if (paramYScaleMap.value[paramName]) {
        delete paramYScaleMap.value[paramName];
      }
    });
    // 清除rectZoomYRanges中不再使用的参数
    Object.keys(rectZoomYRanges.value).forEach(paramName => {
      if (!newSelectedParams.includes(paramName)) {
        delete rectZoomYRanges.value[paramName];
      }
    });
  }

  // 如果参数被清空，清除所有游标标签
  if (!newSelectedParams || newSelectedParams.length === 0) {
    clearCursorLabels();
  }

  selectedParamsInfo.value = JSON.parse(JSON.stringify(props.selectedParametersInfo))
  // 移除selectedParamsInfo中name以"V_"开头的项
  selectedParamsInfo.value = selectedParamsInfo.value.filter((param: any) => !param.name?.startsWith('V_'))
  // 合并props.selectedVirtualParamsInfo
  selectedParamsInfo.value = [...selectedParamsInfo.value, ...(props.selectedVirtualParamsInfo || [])]

  selectedParametersList.value = JSON.parse(JSON.stringify(newSelectedParams))
  // 移除所有以"V_"开头的参数
  selectedParametersList.value = selectedParametersList.value.filter((param: string) => !param.startsWith('V_'))

  selectedParametersList.value = [...selectedParametersList.value, ...props.selectedVirtualParamsName]
  // 视图设置中的参数 
  selectedParametersListInView.value = JSON.parse(JSON.stringify(selectedParametersList.value))
  // 左侧无参数选中但有虚拟参数选中
  if ((!newSelectedParams || newSelectedParams.length === 0) && props.selectedVirtualParamsName.length > 0) {
    selectedParametersList.value.unshift('RecTime')
  }
  // 检查是否有参数从选中变为未选中
  const removedParams = oldSelectedParams?.filter(p => !newSelectedParams?.includes(p)) || []
  
  if (removedParams.length > 0) {
    // 清理被移除参数的标注高亮
    const highlightsToRemove = annotationHighlights.value.filter(highlight =>
      removedParams.includes(highlight.paramName)
    )

    highlightsToRemove.forEach(highlight => {
      if (highlight.highlightPath) {
        highlight.highlightPath.remove()
      }
    })

    // 从annotationHighlights中移除这些高亮
    annotationHighlights.value = annotationHighlights.value.filter(highlight =>
      !removedParams.includes(highlight.paramName)
    )

    // 通知父组件更新标注状态
    emit('parametersRemoved', removedParams)
  }

  // 保存当前的矩形缩放状态和Y轴范围
  const currentIsRectZoomActive = isRectZoomActive.value
  const currentRectZoomYRanges = JSON.parse(JSON.stringify(rectZoomYRanges.value || {}))
  const currentZoomMode = zoomType.value // 添加当前缩放模式
  const isInMeasureMode = interactionMode.value === 'measure' && isMeasuring.value
  
  // 请求接口获取绘图数据
  let paramsDefault = [{ channelId: "RJ", parameters: {} }];

  if (paramsDefault) {
    // 保存当前X轴范围（如果已缩放过）
    const currentXDomain = xScale ? xScale.domain() : initialDomain.value;
    
    // 如果选中的参数列表中没有RecTime，则将其添加到列表中
    if (props.selectedParameters && props.selectedParameters.length > 0) {
      if (!selectedParametersList.value.includes('RecTime')) {
        selectedParametersList.value.unshift('RecTime')
      } else {
        selectedParametersList.value.splice(selectedParametersList.value.indexOf('RecTime'), 1)
        selectedParametersList.value.unshift('RecTime')
      }
    }
    
    emit('update:selectedParametersList', selectedParametersList.value)
    
    // 准备API请求参数
    let startTime: string | undefined = undefined;
    let endTime: string | undefined = undefined;
    let fullData = false;
    // 仅当X轴范围有效时才设置时间范围，否则请求完整数据
    if (currentXDomain && currentXDomain[0] !== Infinity && currentXDomain[1] !== -Infinity && currentXDomain[0] != 'Invalid Date' && currentXDomain[1] != 'Invalid Date') {
      // RJ模式：将Date对象格式化为'YYYY-MM-DD HH:mm:ss'格式
      const formatDateToString = (date: Date | number) => {
        if (date instanceof Date && !isNaN(date.getTime())) {
          return date
            .toLocaleString("zh-CN", {
              year: "numeric",
              month: "2-digit",
              day: "2-digit",
              hour: "2-digit",
              minute: "2-digit",
              second: "2-digit",
            })
            .replace(/\//g, "-");
        }
        return "";
      };
      startTime = formatDateToString(currentXDomain[0] as Date);
      endTime = formatDateToString(currentXDomain[1] as Date);
      if (zoomHistory.value.length == 0) {
        // 非缩放模式，日期清空
        startTime = startTimeLocal.value;
        endTime = endTimeLocal.value;
      }
    } else {
      startTime = startTimeLocal.value
      endTime = endTimeLocal.value
    }

    const params = prepareApiParams(startTime, endTime, fullData)
    
    // 请求主图表数据（当前缩放范围）
    queryChartData(params).then((data) => {
      if (!data || data.length === 0) saveViewSettings();
      // 如果当前没有有效的X轴范围，则计算一个新的
      const isInvalidDomain =
        (currentXDomain[0] instanceof Date &&
          isNaN((currentXDomain[0] as Date).getTime())) ||
        (currentXDomain[1] instanceof Date &&
          isNaN((currentXDomain[1] as Date).getTime())) ||
        currentXDomain[0] === Infinity ||
        currentXDomain[1] === -Infinity;

      if (isInvalidDomain) {
        // RJ模式：处理时间字符串
        let minTime: Date | null = null;
        let maxTime: Date | null = null;

        defaultData.value.forEach((channel: any) => {
          // 遍历该通道的所有参数
          Object.entries(channel.parameters).forEach(
            ([paramId, paramData]: [string, any]) => {
              if (!paramData || !Array.isArray(paramData)) return;

              if (paramData.length > 0) {
                paramData.forEach((d: any) => {
                  // 将时间字符串转换为Date对象
                  const timestamp = new Date(d.timestamp);

                  if (!isNaN(timestamp.getTime())) {
                    // 检查Date是否有效
                    if (!minTime || timestamp < minTime) {
                      minTime = timestamp;
                    }
                    if (!maxTime || timestamp > maxTime) {
                      maxTime = timestamp;
                    }
                  }
                });
              }
            }
          );
        });

        if (minTime && maxTime) {
          // 将时间精确到秒级，去除毫秒
          const roundedMinTime = new Date(Math.floor(minTime.getTime() / 1000) * 1000);
          const roundedMaxTime = new Date( Math.floor(maxTime.getTime() / 1000) * 1000);
          initialDomain.value = [roundedMinTime, roundedMaxTime];
          xScale.domain([roundedMinTime, roundedMaxTime]);
        }
      } else {
        // 保持当前X轴范围
        xScale.domain(currentXDomain);
      }

      // 更新坐标轴
      updateAxes();

      // 更新缩放窗口位置，但不重置到初始状态
      updateZoomWindowPosition();
      if (isChangeTask.value) {
        saveViewSettings(true);
        isChangeTask.value = false;
      } else {
        // 如果处于矩形缩放或垂直缩放状态，恢复之前保存的Y轴范围
        if ((currentIsRectZoomActive || currentZoomMode === "vertical") && Object.keys(currentRectZoomYRanges).length > 0) {
          // 恢复矩形缩放状态
          isRectZoomActive.value = true;
          // 将之前保存的Y轴范围应用到新的参数上
          // 对于新增的参数，如果没有Y轴范围，使用全局Y轴范围或计算新范围
          rectZoomYRanges.value = currentRectZoomYRanges;
          // 使用保存的Y轴范围更新图表
          updateChartWithParamYScales();
        } else {
          // 不在矩形缩放测量模式下，使用普通更新
          // updateChart(false)
          saveViewSettings();
        }
      }

      // 延迟处理，确保图表已更新
      setTimeout(() => {
        // 如果在测量模式下，恢复测量游标
        if (isMeasuring.value) {
          // 确保有保存的游标位置
          if (!cursor1Position.value) cursor1Position.value = initialDomain.value[0];
          if (!cursor2Position.value) cursor2Position.value = initialDomain.value[1];
          
          console.log('从无参数到有参数，恢复测量游标', cursor1Position.value, cursor2Position.value)
          updateMeasurementCursors();
          if (selectedParametersList.value && selectedParametersList.value.length > 0) {
            updateMeasurementResults();
          }
        } else if (currentCursorPosition.value != undefined) {
          // 如果在普通游标模式下，恢复普通游标
          console.log("恢复游标线:", currentCursorPosition.value);
          const x = xScale(currentCursorPosition.value);
          updateCursorPosition(x, currentCursorPosition.value);
        }
      }, 300);
    });

    // 同时请求概览图数据（完整范围）
    if (zoomHistory.value.length > 0) {
      queryOverviewData(params).then((data) => {
        // 数据加载完成后显示缩放带概览图
        nextTick(() => {
          if (svg) {
            const zoomBarGroup = svg.select(".zoom-bar-group");
            if (!zoomBarGroup.empty() && chartContainer.value) {
              try {
                const width = chartContainer.value.clientWidth;
                const height = chartContainer.value.clientHeight;
                // 在缩放带上显示整体数据概览
                updateOverviewChart(zoomBarGroup as d3.Selection<SVGGElement, unknown, null, undefined>, width, height)
              } catch (error) {
                console.error('更新缩放带概览图出错:', error)
              }
            }
          }
        });
      });
    }
  }
  
  // 如果没有选中参数，或有参数被移除，强制清除离散区域
  if (!newSelectedParams || newSelectedParams.length === 0 || removedParams.length > 0) {
    if (svg) {
      // 彻底清除所有与离散区域相关的元素
      svg.selectAll('.discrete-band').remove()
      svg.selectAll('.discrete-label').remove()
      svg.selectAll('.param-label-overlay').remove()
      svg.selectAll('.channel-param-label').remove()
      
      // 隐藏离散区域
      svg.select('.discrete-group').style('visibility', 'hidden')
      svg.select('.discrete-background').style('visibility', 'hidden')
      
      // 确保离散区域完全重绘
      nextTick(() => {
        const x = xScale(currentCursorPosition.value)
        console.log('恢复游标线:', currentCursorPosition.value, x)
        updateCursorPosition(x, currentCursorPosition.value)
        updateDiscreteArea()
        updateZoomBarPosition()
      })
    }
    // 重置测量数据显示，但不重置游标位置
    if (isMeasuring.value) {
      emit('update:measurementData', undefined)
    }
  }
}, { deep: true })

// 更新缩放带位置的函数
const updateZoomBarPosition = () => {
  if (!svg || !chartContainer.value) return
  
  // 获取主图表高度
  const containerHeight = chartContainer.value.clientHeight
  const width = chartContainer.value.clientWidth
  const mainChartHeight = containerHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 更新SVG的viewBox属性，确保整个图表区域能够正确显示
  svg.attr('viewBox', `0 0 ${width} ${mainChartHeight + discreteAreaHeight.value + faultBandsAreaHeight.value + zoomBarHeight.value}`)
  
  // 更新离散区域组的位置
  svg.select('.discrete-group')
    .attr('transform', `translate(0, ${mainChartHeight})`)
  
  // 更新离散区域背景高度
  svg.select('.discrete-background')
    .attr('height', discreteAreaHeight.value)
  
  // 获取缩放带正确的Y位置
  const newY = getZoomBarY()
  
  // 更新缩放带组的位置
  svg.select('.zoom-bar-group')
    .attr('transform', `translate(0, ${newY})`)
    
  // 更新缩放带剪切区域的位置
  svg.select('#zoom-bar-area rect')
    .attr('y', newY)
    
  // 更新X轴位置，确保它随离散区域高度变化而调整
  svg.select('.x-axis')
    .attr('transform', `translate(0,${mainChartHeight - margin.bottom})`)
  
  // 更新裁剪区域
  updateClipPath(width, mainChartHeight)
}

// 创建一个标志变量，表示当前是否处于矩形缩放模式
const isRectZoomActive = ref(false);

// 高亮显示指定参数的Y轴
const showParameterYAxis = (paramId: string) => {
  if (useSharedYAxis.value) return// 共享y轴没有此功能
  if (!svg) return
  console.log('显示参数Y轴:', paramId)
  
  // 隐藏初始化的y轴
  svg.select('.initial-y-axis').style('display', 'none')
  
  // 查找所有Y轴，将它们的不透明度设置为0（隐藏）
  svg.selectAll('.param-y-axis')
    .style('opacity', 0)
  
  // 同样处理Y轴背景
  svg.selectAll('.y-axis-background')
    .style('opacity', 0)
  
  // 显示指定参数的Y轴，使用escapeSelector处理参数ID
  const escapedParamId = escapeSelector(paramId);
  svg.select(`.param-${escapedParamId}`)
    .style('opacity', 1)
}

// 添加全局mouseup事件监听器
const addGlobalMouseUpListener = () => {
  window.addEventListener('mouseup', handleMouseUp)
}

// 在组件卸载时移除事件监听器
const removeGlobalMouseUpListener = () => {
  window.removeEventListener('mouseup', handleMouseUp)
}

// 在组件挂载和卸载时添加/移除全局事件监听器
onMounted(() => {
  
  // 添加键盘事件监听器
  window.addEventListener('keydown', handleKeyDown)
  
  // 添加全局mouseup事件监听器
  addGlobalMouseUpListener()
  
  // 添加窗口大小变化的监听器，使用防抖函数
  window.addEventListener('resize', debounce(handleResize, 200))
  
  // 初始化图表和游标
  nextTick(() => {
    // 确保在DOM更新后再初始化图表
    if (chartContainer.value) {
      initChartAndCursor()
      // 确保组件获得焦点以接收键盘事件
      setFocus()
    }
  })
  
  // 应用初始主题设置
  // if (dataViewerContainer.value) {
  //   if (isDarkMode.value) {
  //     dataViewerContainer.value.classList.add('dark-mode')
  //   } else {
  //     dataViewerContainer.value.classList.remove('dark-mode')
  //   }
  // }
  
  // 添加图表点击事件监听
  if (chartContainer.value) {
    chartContainer.value.addEventListener('click', handleChartClick)
  }
})

onBeforeUnmount(() => {
  // 移除键盘事件监听器
  window.removeEventListener('keydown', handleKeyDown)
  
  // 移除全局mouseup事件监听器
  removeGlobalMouseUpListener()
  
  // 移除窗口大小变化监听器
  window.removeEventListener('resize', handleResize)
  
  // 移除图表点击事件监听
  if (chartContainer.value) {
    chartContainer.value.removeEventListener('click', handleChartClick)
  }
  // 移除所有可能存在的滚动容器
  const scrollContainers = document.querySelectorAll('.discrete-scroll-container')
  scrollContainers.forEach(container => {
    if (container.parentNode) {
      container.parentNode.removeChild(container)
    }
  })
})

// 窗口大小变化处理函数，使用节流函数避免过于频繁的重绘
const handleResize = debounce(() => {
  if (svg && chartContainer.value) {
    // 获取新的宽度和高度
    const width = chartContainer.value.clientWidth
    const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
    
    // 更新X轴比例尺的范围
    if (xScale) {
      xScale.range([margin.left, width - margin.right])
    }
    
    // 更新坐标轴
    updateAxes()
    
    // 更新缩放带背景和相关元素的宽度
    svg.select('.zoom-bar-background')
      .attr('width', width - margin.left - margin.right)
    
    svg.select('.zoom-bar-overlay')
      .attr('width', width - margin.left - margin.right)
    
    // 更新缩放带右手柄位置
    svg.select('.zoom-handle-right')
      .attr('x', width - margin.right - 4)
    
    // 重新绘制图表
    updateChart(false)
    
    // 更新缩放带和离散区域位置
    updateZoomBarPosition()
    
    // 更新缩放窗口位置
    updateZoomWindowPosition()
  }
}, 150) // 150毫秒的延迟

// 简单的节流函数
function debounce(fn: Function, delay: number) {
  let timer: number | null = null
  return function(...args: any[]) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn(...args)
      timer = null
    }, delay) as unknown as number
  }
}

// 获取特定通道的设置
const getChannelSetting = (paramName: string, channelId: string) => {
  if (!props.viewSettings) return null;
  const settingKey = `${paramName}:${channelId}`;
  return props.viewSettings[settingKey] || null;
}

// HSL转十六进制颜色格式
const hslToHex = (h: number, s: number, l: number): string => {
  l /= 100;
  const a = s * Math.min(l, 1 - l) / 100;
  const f = (n: number) => {
    const k = (n + h / 30) % 12;
    const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
    return Math.round(255 * color).toString(16).padStart(2, '0');
  };
  return `#${f(0)}${f(8)}${f(4)}`;
}

// 参数颜色映射缓存，确保每次调用返回相同颜色
const paramColorCache: Record<string, string> = {};

// 可用固定颜色列表
const availableColors = [
  '#0080FF', // 天蓝色
  '#FF80FF', // 粉红色
  '#00FF00', // 纯绿色
  '#f36176', // 红色
  '#FFFF00', // 纯黄色
  '#8080FF', // 淡紫色
  '#00FFFF', // 青色
  '#FF8000', // 橙色
  '#800080', // 紫色
  '#008000', // 深绿色
  '#008080', // 墨绿色
  '#5500FF', // 靛蓝色
  '#804040',  // 红褐色
  '#80FF80', // 浅绿色
  '#FF0080', // 玫红色
];

// 当前已分配的颜色索引
let nextColorIndex = 0;

// 为参数生成颜色的函数
const generateColorForParameter = (paramId: string): string => {
  // RecTime固定使用深灰色
  const updatedSettings = { ...props.viewSettings }
  // 所有通道颜色都要更新
  const channelKey = [`${paramId}:leftA`, `${paramId}:leftB`, `${paramId}:rightA`, `${paramId}:rightB`, `${paramId}:RJ`]
  if (paramId === 'RecTime') {
    channelKey.forEach(key => {
      updatedSettings[key] = {
        ...updatedSettings[key],
        color: '#858585'
      };
    })
    paramColorCache[paramId] = '#858585';
    emit('update:viewSettings', updatedSettings)
    return '#858585';
  }
  
  // 如果已经为该参数分配了颜色，直接返回
  if (paramColorCache[paramId]) {
    channelKey.forEach(key => {
      updatedSettings[key] = {
        ...updatedSettings[key],
        color: paramColorCache[paramId]
      };
    })
    return paramColorCache[paramId];
  }
  
  // 分配新颜色 - 顺序从可用颜色列表中选择
  let color = availableColors[nextColorIndex % availableColors.length];
  // 更新颜色索引
  nextColorIndex++;
  // 如果color和viewSettings中存在的color相同，则重新选择
  if (nextColorIndex < availableColors.length) {
    Object.keys(updatedSettings).forEach(key => {
      if (updatedSettings[key]?.color === color) {
        const newColor = generateColorForParameter(paramId);
        color = newColor;
      }
    })
  }
  
  // 缓存并返回颜色
  paramColorCache[paramId] = color;
  
  channelKey.forEach(key => {
    updatedSettings[key] = {
      ...updatedSettings[key],
      color: color
    };
  })
  // 将颜色存入viewsettings
  emit('update:viewSettings', updatedSettings)
  return color;
}

// 获取通道设置的颜色 - 确保返回当前实际使用的颜色
const getChannelSettingColor = (paramName: string, channelId: string): string => {
  // 先从现有设置中查找
  if (props.viewSettings) {
    const settingKey = `${paramName}:${channelId}`;
    if (props.viewSettings[settingKey]?.color) {
      // 对于非leftA通道，应用深浅变化
      if (channelId !== 'leftA' && channelId !== 'RJ') {
        // 获取leftA的颜色作为基础颜色
        const baseColorKey = `${paramName}:leftA`;
        const baseColor = props.viewSettings[baseColorKey]?.color || 
                          generateColorForParameter(paramName);
        
        // 获取该参数所有有数据的通道
        const channelsWithData = getChannelsWithData(paramName);
        const channelIndex = channelsWithData.indexOf(channelId);
        const totalChannels = channelsWithData.length;
        
        // 生成颜色变体
        if (totalChannels > 1 && channelIndex > 0) {
          return generateChannelColorVariant(baseColor, channelIndex, totalChannels);
        }
      }
      return props.viewSettings[settingKey].color;
    }
  }
  
  // 如果找不到预设，使用getChannelColor函数生成颜色
  return getChannelColor(channelId, paramName);
}

// 获取通道设置的线宽
const getChannelSettingLineWidth = (paramName: string, channelId: string): number => {
  const setting = getChannelSetting(paramName, channelId);
  return setting?.lineWidth || 1.5;
}

// 获取通道设置的显示模式
const getChannelSettingDisplayMode = (paramName: string, channelId: string): 'continuous' | 'discrete' => {
  const setting = getChannelSetting(paramName, channelId);
  if (setting?.displayMode) return setting.displayMode;
  
  // 默认显示模式 - 根据参数的类型决定
  return isDiscreteParameter(paramName) ? 'discrete' : 'continuous';
}

// 获取通道设置的可见性
const getChannelSettingVisible = (paramName: string, channelId: string): boolean => {
  // 如果该通道有数据，则显示
  if (hasDataForChannel(paramName, channelId)) {
    return true;
  } else {
    return false;
  }
}

// 获取参数名称函数
const getParameterName = (paramName: string): string => {
  return paramName
}

// 判断参数是否为离散类型 - 通过参数的type属性判断，而不是通过参数ID
const isDiscreteParameter = (paramName: string): boolean => {
  // 从参数列表中查找
  const parameter = selectedParamsInfo.value?.find(p => p.name === paramName)
  if (parameter) {
    return parameter.displayType === 'discrete'
  }
  // 如果找不到参数信息，则默认为连续量类型
  return false
}

// 检查某个参数在特定通道中是否有数据
const hasDataForChannel = (paramName: string, channelId: string): boolean => {
  // 找到对应通道
  const channel = defaultData.value.find(ch => ch.channelId === channelId)
  if (!channel) return false
  
  // 检查该通道中是否有该参数的数据
  const paramData = channel.parameters[paramName]
  return !!paramData && Array.isArray(paramData) && paramData.length > 0
}

// 获取某个参数有数据的所有通道
const getChannelsWithData = (paramName: string): string[] => {
  return ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].filter(channelId => 
    hasDataForChannel(paramName, channelId)
  )
}

// 监听侧边栏折叠状态变化
watch(() => props.isSiderCollapsed, () => {
  // 保存当前状态
  const currentDomain = xScale ? xScale.domain() : initialDomain.value;
  const currentCursor = currentCursorPosition.value;
  const isInMeasureMode = interactionMode.value === 'measure' && isMeasuring.value;
  const cursor1 = cursor1Position.value;
  const cursor2 = cursor2Position.value;

  // 延迟执行，等待DOM更新
  setTimeout(() => {
    if (!chartContainer.value) return;
    
    console.log('侧边栏状态变化，正在重绘图表...');
    
    // 重新初始化图表
    initChart();
    
    // 恢复之前的缩放状态
    if (currentDomain[0] !== Infinity && currentDomain[1] !== -Infinity) {
      xScale.domain(currentDomain);
    }
    
    // 更新图表但不重新请求数据
    updateChart(false);
    
    // 恢复游标位置
    if (isInMeasureMode) {
      // 恢复测量模式和游标位置
      cursor1Position.value = cursor1;
      cursor2Position.value = cursor2;
      activeCursor.value = 'cursor1'; // 默认激活第一个游标
      isMeasuring.value = true;
      updateMeasurementCursors();
      updateMeasurementResults();
    } else if (currentCursor !== null) {
      // 恢复普通游标位置
      currentCursorPosition.value = currentCursor;
      const cursorX = xScale(currentCursor);
      updateCursorPosition(cursorX, currentCursor);
    }
    
    // 更新缩放窗口位置
    updateZoomWindowPosition();
  }, 350);
}, { immediate: false });

// 更新缩放窗口位置的函数
const updateZoomWindowPosition = () => {
  if (!svg) return;
  
  // 获取当前的X轴范围
  const [currentMin, currentMax] = xScale.domain();
  
  // 获取缩放带的初始范围（全局数据范围）
  const [zoomBarMin, zoomBarMax] = initialDomain.value;
  
  // 计算缩放窗口在缩放带中的位置比例
  const leftRatio = (currentMin - zoomBarMin) / (zoomBarMax - zoomBarMin);
  const rightRatio = (currentMax - zoomBarMin) / (zoomBarMax - zoomBarMin);
  
  // 获取缩放带的宽度
  const zoomBarWidth = parseInt(svg.select('.zoom-bar-background').attr('width'));
  
  // 计算缩放窗口的左边界和宽度
  const zoomWindowLeft = margin.left + leftRatio * zoomBarWidth;
  const zoomWindowWidth = (rightRatio - leftRatio) * zoomBarWidth;
  
  // 更新缩放窗口位置
  svg.select('.zoom-window')
    .attr('x', zoomWindowLeft)
    .attr('width', zoomWindowWidth);
  
  // 更新左右手柄位置
  svg.select('.zoom-handle-left')
    .attr('x', zoomWindowLeft);
  
  svg.select('.zoom-handle-right')
    .attr('x', zoomWindowLeft + zoomWindowWidth - 4); // 减去手柄宽度
}

// 重置缩放窗口位置的函数
const resetZoomWindowPosition = () => {
  if (!svg) return;
  
  // 获取缩放带的宽度
  const zoomBarBackground = svg.select('.zoom-bar-background');
  if (zoomBarBackground.empty()) return;
  
  const zoomBarWidth = parseInt(zoomBarBackground.attr('width'));
  if (isNaN(zoomBarWidth)) return;
  
  // 获取缩放窗口和手柄
  const zoomWindow = svg.select('.zoom-window');
  const zoomHandleLeft = svg.select('.zoom-handle-left');
  const zoomHandleRight = svg.select('.zoom-handle-right');
  
  if (zoomWindow.empty() || zoomHandleLeft.empty() || zoomHandleRight.empty()) return;
  
  // 重置缩放窗口为全宽
  zoomWindow
    .attr('x', margin.left)
    .attr('width', zoomBarWidth);
  
  // 更新左右手柄位置
  zoomHandleLeft
    .attr('x', margin.left);
  
  zoomHandleRight
    .attr('x', margin.left + zoomBarWidth - 4); // 减去手柄宽度
}

// 在缩放带上显示概览图
const updateOverviewChart = (zoomBarGroup: d3.Selection<SVGGElement, unknown, null, undefined>, width: number, height: number) => {
  // 移除之前的概览图
  zoomBarGroup.selectAll('.overview-line').remove()
  
  // 计算概览图的高度
  const overviewHeight = zoomBarHeight.value - 10
  
  // 为每个参数绘制概览线
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || []
  
  // 确保有概览数据
  if (!outViewData.value || outViewData.value.length === 0) {
    console.warn('没有概览图数据可用');
    return;
  }
  
  // 为每个通道每个参数创建概览线
  outViewData.value.forEach((channel: {channelId: string, parameters: Record<string, Array<{timestamp: number, value: number}>>}) => {
    const channelId = channel.channelId
    
    // 遍历该通道的所有参数
    Object.entries(channel.parameters).forEach(([paramId, paramData]) => {
      // 跳过RecTime参数，除非它是被选中的
      if (paramId === 'RecTime' && !selectedParametersListInView.value?.includes('RecTime')) return

      // 跳过离散参数和无数据参数
      if (isDiscreteParameter(paramId) || !paramData || !Array.isArray(paramData) || paramData.length === 0) return
      
      // 跳过未选中的参数
      if (!selectedParams.includes(paramId)) return
      
      // 确保参数可见
      if (!getChannelSettingVisible(paramId, channelId)) return
      
      // 创建临时X和Y比例尺
      let overviewXScale : d3.ScaleLinear<number, number> | d3.ScaleTime<number, number>
  
      overviewXScale = d3.scaleTime()
        .domain([new Date(initialDomain.value[0]), new Date(initialDomain.value[1])])
        .range([margin.left, width - margin.right])
      
      // 计算Y轴数据范围
      const values = paramData.map((d: {value: number}) => d.value)
      const yMin = Math.min(...values)
      const yMax = Math.max(...values)
      
      const overviewYScale = d3.scaleLinear()
        .domain([yMin, yMax])
        .range([overviewHeight, 0])
      
      // 创建线生成器
      const line = d3.line<{timestamp: number | string, value: number}>()
        .x(d => {
          // RJ模式：将时间字符串转换为Date对象
          return overviewXScale(new Date(d.timestamp as string))
        })
        .y(d => overviewYScale(d.value))
        .curve(d3.curveLinear)
      
      // 绘制概览线
      zoomBarGroup.append('path')
        .attr('class', 'overview-line')
        .attr('d', line(paramData))
        .attr('fill', 'none')
        .attr('stroke', getChannelSettingColor(paramId, channelId))
        .attr('stroke-width', 1)
        .attr('opacity', 0.5)
    })
  })
  
  // 删除之前的交互区域
  zoomBarGroup.selectAll('.zoom-bar-overlay').remove()
  zoomBarGroup.selectAll('.zoom-window').remove()
  zoomBarGroup.selectAll('.zoom-handle').remove()
  
  // 添加缩放带的交互区域
  const zoomBarOverlay = zoomBarGroup.append('rect')
    .attr('class', 'zoom-bar-overlay')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', 'none')
    .attr('pointer-events', 'all')

  // 添加可拖动的视窗指示器
  const zoomWindow = zoomBarGroup.append('rect')
    .attr('class', 'zoom-window')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', width - margin.left - margin.right)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', 'rgba(173, 216, 230, 0.4)')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
    
  // 添加缩放带的拖动区域句柄
  const zoomHandleLeft = zoomBarGroup.append('rect')
    .attr('class', 'zoom-handle zoom-handle-left')
    .attr('x', margin.left)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', 4)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', '#1890ff')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
    
  const zoomHandleRight = zoomBarGroup.append('rect')
    .attr('class', 'zoom-handle zoom-handle-right')
    .attr('x', width - margin.right - 4)
    .attr('y', 0) // 从0开始，因为已经通过transform调整了位置
    .attr('width', 4)
    .attr('height', zoomBarHeight.value - 10)
    .attr('fill', '#1890ff')
    .attr('stroke', '#1890ff')
    .attr('stroke-width', 1)
    .attr('rx', 3) // 添加圆角
  
  // 更新缩放窗口位置
  updateZoomWindowPosition()

  // 设置缩放带交互
  setupZoomBarInteractions(zoomBarOverlay, zoomWindow, zoomHandleLeft, zoomHandleRight)
}

// 添加一个函数计算数据点之间的平均时间间隔
const calculateTimeStep = (): number => {
  // 默认的时间步长，如果无法计算或大于此值则使用这个值
  return 1
  const defaultStep = 0.1;
  
  // 检查是否有数据
  if (!defaultData.value || defaultData.value.length === 0) {
    return defaultStep;
  }
  
  // 获取当前选中的参数列表（不包括RecTime，除非它是被选中的）
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || []
  
  if (selectedParams.length === 0) {
    return defaultStep;
  }
  
  // 记录找到的最小时间间隔
  let minTimeIntervalList: number[] = [];
  
  // 如果找到了有效的时间间隔，则使用它
  // 否则使用默认步长
  timeIntervalList.value.forEach((item: number) => {
    // 定义最大公约数函数
    function gcd(a: number, b: number): number {
      if (!b) return a;
      return gcd(b, a % b);
    }
    if (item != 0 && item <= 0.1) {
      minTimeIntervalList.push(Number(item.toFixed(3)))
    } else if (item == 0) {
      minTimeIntervalList.push(0.1)
    } else if (item > 0.1) {
      // 转化为毫秒，算最大公约数作为最终步长
      const timeInterval = [0.1, item].map(item => item * 1000)
      const gcdValue = gcd(timeInterval[0], timeInterval[1])
      minTimeIntervalList.push(Number((gcdValue / 1000).toFixed(3)))
    }
  })
  console.log(minTimeIntervalList, Math.min(...minTimeIntervalList))
  return Math.min(...minTimeIntervalList)
}

// 获取当前的时间步长
const getTimeStep = (): number => {
  // 计算基于数据的时间步长
  const calculatedStep = calculateTimeStep();
  return calculatedStep
}

// 获取参数的基础颜色
const getParameterBaseColor = (paramName: string): string => {
  if (props.viewSettings) {
    const settingKey = `${paramName}:leftA`;
    if (props.viewSettings[settingKey]?.color) {
      return props.viewSettings[settingKey].color;
    }
  }
  return generateColorForParameter(paramName);
}

// 获取参数的线宽
const getParameterLineWidth = (paramName: string): number => {
  const setting = getChannelSetting(paramName, 'leftA');
  return setting?.lineWidth || 1.5;
}

// 获取参数的显示模式
const getParameterDisplayMode = (paramName: string): 'continuous' | 'discrete' => {
  const setting = getChannelSetting(paramName, 'leftA');
  return setting?.displayMode || 'continuous';
}

// 获取参数是否显示标签
const getParameterShowLabel = (paramName: string): boolean => {
  const setting = getChannelSetting(paramName, 'leftA');
  return setting?.showLabel || false;
}

// 更新参数设置 - 这个函数同时修改ViewSettings和重绘图表
const updateParameterSetting = (paramName: string, updates: Partial<ParameterViewSetting>) => {
  if (!props.viewSettings) return;
  
  const updatedSettings = { ...props.viewSettings };
  
  // 获取该参数所有有数据的通道
  const allChannelIds = ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'];
  
  // 如果是更新displayMode，则应用到所有通道
  if ('displayMode' in updates) {
    // 对所有通道应用相同的displayMode设置
    allChannelIds.forEach(channelId => {
      const settingKey = `${paramName}:${channelId}`;
      
      // 如果设置不存在，创建新设置
      if (!updatedSettings[settingKey]) {
        updatedSettings[settingKey] = {
          parameterId: paramName,
          channelId,
          displayMode: updates.displayMode as 'continuous' | 'discrete',
          lineWidth: 1.5
        };
      } else {
        // 更新现有设置
        updatedSettings[settingKey] = {
          ...updatedSettings[settingKey],
          displayMode: updates.displayMode as 'continuous' | 'discrete'
        };
      }
    });
  } else {
    // 只更新主通道设置（leftA）
    const mainKey = `${paramName}:leftA`;
    if (!updatedSettings[mainKey]) {
      updatedSettings[mainKey] = {
        parameterId: paramName,
        channelId: 'leftA',
        displayMode: isDiscreteParameter(paramName) ? 'discrete' : 'continuous',
        lineWidth: 1.5,
        ...updates
      };
    } else {
      updatedSettings[mainKey] = {
        ...updatedSettings[mainKey],
        ...updates
      };
    }
    
    // 如果是更新颜色，根据主通道颜色为其他通道计算派生颜色
    if (updates.color) {
      const channelsWithData = getChannelsWithData(paramName);
      channelsWithData.forEach((channelId, index) => {
        if (channelId !== 'leftA' && channelId !== 'RJ') {
          const channelKey = `${paramName}:${channelId}`;
          const baseColor = updates.color;
          
          // 生成颜色变体
          const variantColor = generateChannelColorVariant(baseColor!, index, channelsWithData.length);
          
          // 更新通道颜色
          if (!updatedSettings[channelKey]) {
            updatedSettings[channelKey] = {
              parameterId: paramName,
              channelId,
              displayMode: updatedSettings[mainKey].displayMode,
              lineWidth: 1.5,
              color: variantColor
            };
          } else {
            updatedSettings[channelKey] = {
              ...updatedSettings[channelKey],
              color: variantColor
            };
          }
        }
      });
    }
  }
  
  // 发送更新事件
  emit('update:viewSettings', updatedSettings);
  
  // 立即重新绘制图表以实时预览效果
  // updateChart(false);
}

// 获取参数的Y轴最小值
const getParameterYAxisMin = (paramName: string): number | undefined => {
  const setting = getChannelSetting(paramName, 'leftA');
  return setting?.yAxisMin;
}

// 获取参数的Y轴最大值
const getParameterYAxisMax = (paramName: string): number | undefined => {
  const setting = getChannelSetting(paramName, 'leftA');
  return setting?.yAxisMax;
}

// 更新游标点方法，添加裁剪，并在所有曲线上显示游标点
const updateCursorPoints = (timestamp: number | Date) => {
  if (!svg) return
  
  // 在RJ模式下，将Date转换为毫秒数进行统一处理
  let numericTimestamp: number;
  numericTimestamp = timestamp instanceof Date ? timestamp.getTime() : new Date(timestamp as string).getTime();
  
  // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || []
  
  if (selectedParams.length === 0) return
  
  const height = chartContainer.value!.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 为每个选中的参数和每个通道显示游标点
  selectedParams.forEach((paramName) => {
    // 跳过离散参数
    if (getParameterDisplayMode(paramName) !== 'continuous') return
    
    // 获取该参数正确的Y轴比例尺 - 使用与线条绘制相同的比例尺
    let paramYScale: d3.ScaleLinear<number, number> | undefined
    
    // 首先检查用户是否在视图设置中设置了Y轴范围
    const yAxisMin = getParameterYAxisMin(paramName)
    const yAxisMax = getParameterYAxisMax(paramName)
    
    // 如果有用户设置的Y轴范围（上限或下限至少设置了一个），则优先使用
    if (yAxisMin !== undefined || yAxisMax !== undefined) {
      // 获取当前参数的可见数据范围，用于补充未设置的边界
      let computedMin = Infinity
      let computedMax = -Infinity
      let hasVisibleData = false
      
      defaultData.value.forEach(channel => {
        const channelId = channel.channelId
        const paramData = channel.parameters[paramName]
        
        if (!paramData || !Array.isArray(paramData)) return
        
        // 检查此参数在此通道的显示方式
        const displayMode = getChannelSettingDisplayMode(paramName, channelId)
        if (displayMode === 'discrete') return
        
        // 检查通道可见性
        if (!getChannelSettingVisible(paramName, channelId)) return
        
        // 获取当前x轴范围内的数据
        const xDomain = xScale.domain()
        const visibleData = paramData.filter(d => {
          // 将时间字符串转换为Date进行比较
          const dataTime = new Date(d.timestamp).getTime()
          const domainStart = (xDomain[0] as Date).getTime()
          const domainEnd = (xDomain[1] as Date).getTime()
          return dataTime >= domainStart && dataTime <= domainEnd
          
        })
        
        if (visibleData.length > 0) {
          // 如果value为NaN，则不参与计算
          const dataMin = Math.min(...visibleData.map(d => d.value === 'NaN' ? Infinity : d.value))
          const dataMax = Math.max(...visibleData.map(d => d.value === 'NaN' ? -Infinity : d.value))
          computedMin = Math.min(computedMin, dataMin)
          computedMax = Math.max(computedMax, dataMax)
          hasVisibleData = true
        }
      })
      
      // 添加一些边距使图表更美观
      if (hasVisibleData) {
        const valueRange = computedMax - computedMin
        const padding = valueRange * 0.2 // 上下各预留10%空间
        computedMin -= padding
        computedMax += padding
      }
      
      // 使用用户设置的范围，如果某个边界未设置，则使用计算的值
      const finalMin = yAxisMin !== undefined ? yAxisMin : (hasVisibleData ? computedMin : 0)
      const finalMax = yAxisMax !== undefined ? yAxisMax : (hasVisibleData ? computedMax : 100)
      
      paramYScale = d3.scaleLinear()
        .domain([finalMin, finalMax])
        .range([height - margin.bottom, margin.top])
      
      console.log(`游标点使用用户设置的Y轴范围 - ${paramName}:`, 
        [finalMin, finalMax], 
        `(用户设置: min=${yAxisMin}, max=${yAxisMax})`)
    }
    // 如果在矩形缩放模式下，使用矩形缩放的Y轴范围
    else if (isRectZoomActive.value && rectZoomYRanges.value && rectZoomYRanges.value[paramName]) {
      const yRange = rectZoomYRanges.value[paramName]
      paramYScale = d3.scaleLinear()
        .domain(yRange)
        .range([height - margin.bottom, margin.top])
        
      console.log(`游标点使用矩形缩放Y轴范围 - ${paramName}:`, yRange)
    } else {
      // 获取当前参数的可见数据范围
      let minValue = Infinity
      let maxValue = -Infinity
      let hasData = false
      
      defaultData.value.forEach(channel => {
        const channelId = channel.channelId
        const paramData = channel.parameters[paramName]
        
        if (!paramData || !Array.isArray(paramData)) return
        
        // 检查此参数在此通道的显示方式
        const displayMode = getChannelSettingDisplayMode(paramName, channelId)
        if (displayMode === 'discrete') return
        
        // 检查通道可见性
        if (!getChannelSettingVisible(paramName, channelId)) return
        
        // 获取当前x轴范围内的数据
        const xDomain = xScale.domain()
        const visibleData = paramData.filter(d => {
          // 将时间字符串转换为Date进行比较
          const dataTime = new Date(d.timestamp).getTime()
          const domainStart = (xDomain[0] as Date).getTime()
          const domainEnd = (xDomain[1] as Date).getTime()
          return dataTime >= domainStart && dataTime <= domainEnd
          
        })
        
        if (visibleData.length > 0) {
          const dataMin = Math.min(...visibleData.map(d => d.value === 'NaN' ? Infinity : d.value))
          const dataMax = Math.max(...visibleData.map(d => d.value === 'NaN' ? -Infinity : d.value))
          minValue = Math.min(minValue, dataMin)
          maxValue = Math.max(maxValue, dataMax)
          hasData = true
        }
      })
      
      // 如果找到了有效的最大值和最小值
      if (hasData && minValue !== Infinity && maxValue !== -Infinity) {
        const valueRange = maxValue - minValue
        
        // 扩展范围，使曲线显示在图表中间
        const paddingFactor = 0.2 // 上下各预留1/10
        const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
        
        // 计算Y轴范围
        const yMin = minValue - padding
        const yMax = maxValue + padding
        
        // 创建参数专用的Y轴比例尺
        paramYScale = d3.scaleLinear()
          .domain([yMin, yMax])
          .range([height - margin.bottom, margin.top])
      } else {
        // 如果没有可见数据，使用全局Y轴
        paramYScale = yScale
      }
    }
    
    if (!paramYScale) return // 如果没有有效的比例尺则跳过
    
    // 遍历所有通道
    defaultData.value.forEach(channel => {
      const channelId = channel.channelId
      
      // 检查该参数在该通道的显示模式
      const displayMode = getChannelSettingDisplayMode(paramName, channelId)
      
      // 如果是离散显示模式，则不添加游标点
      if (displayMode === 'discrete') return
      
      // 检查参数是否可见
      if (!getChannelSettingVisible(paramName, channelId)) return
      
      const paramData = channel.parameters[paramName]
      if (paramData && paramData.length > 0) {
        // 使用线性插值法计算游标位置的值，针对RJ模式进行适配
        // 找到游标左右两侧最近的点
        // 初始化左右点
        let leftPoint = null;
        let rightPoint = null;
        
        // 遍历查找左右点
        for (const point of paramData) {
          const timestamp = new Date(point.timestamp as string).getTime()
          if (timestamp <= numericTimestamp) {
            leftPoint = point;
          } else {
            rightPoint = point;
            break;
          }
        }
        
        // 处理边界情况
        if (!leftPoint && rightPoint) leftPoint = rightPoint;
        if (!rightPoint && leftPoint) rightPoint = leftPoint;
        
        let interpolatedValue = 0
        
        // 获取左右点的时间戳数值
        const leftTimestamp = new Date(leftPoint.timestamp as string).getTime()
        const rightTimestamp = new Date(rightPoint.timestamp as string).getTime()
        
        // 如果找到了左右两个点，使用线性插值
        if (leftTimestamp !== new Date('1900-01-01 00:00:00').getTime() &&
            rightTimestamp !== new Date('2200-01-01 00:00:00').getTime()) {
          // 如果左右点是同一个点，或者游标恰好在某个点上，直接使用该点的值
          if (leftTimestamp === rightTimestamp || 
              leftTimestamp === numericTimestamp || 
              rightTimestamp === numericTimestamp) {
            interpolatedValue = leftTimestamp === numericTimestamp ? 
                                leftPoint.value : 
                                rightPoint.value
          } else {
            // 区分离散量和连续量
            const showType = selectedParamsInfo.value.find(item => item.name === paramName)?.mode
            if (showType === 1) {
              interpolatedValue = leftPoint.value
            } else {
              // 线性插值公式: y = y1 + (x - x1) * (y2 - y1) / (x2 - x1)
              const ratio = (numericTimestamp - leftTimestamp) / (rightTimestamp - leftTimestamp)
              interpolatedValue = leftPoint.value + ratio * (rightPoint.value - leftPoint.value)
            }
          }
        } else if (leftTimestamp !== new Date('1900-01-01 00:00:00').getTime()) {
          // 如果只找到左侧点，使用左侧点的值
          interpolatedValue = leftPoint.value
        } else if (rightTimestamp !== new Date('2200-01-01 00:00:00').getTime()) {
          // 如果只找到右侧点，使用右侧点的值
          interpolatedValue = rightPoint.value
        }
      }
    })
  })
}

// 添加一个变量记录游标在屏幕上的相对位置比例
const cursorScreenPositionRatio = ref(0.5); // 默认在屏幕中间(50%)

// 添加一个变量记录测量游标在屏幕上的相对位置比例
const cursor1ScreenRatio = ref(0.5);

// 添加一个变量记录测量游标在屏幕上的相对位置比例
const cursor2ScreenRatio = ref(0.5);

// 在script中添加新的变量来保存矩形缩放时每个参数的Y轴范围
const rectZoomYRanges = ref<Record<string, [number, number]>>({});

// 添加一个用于矩形缩放后更新图表的函数，使用框选的Y轴范围
const updateChartWithParamYScales = () => {
  if (!svg || !chartContainer.value) return;

  const width = chartContainer.value.clientWidth;
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value;
  if (props.viewSettings) {
    // 计算离散区域的实际高度，根据显示的通道个数动态变化

    // 获取选中的参数列表（不包括RecTime，除非它是被选中的）
    const selectedParams = selectedParametersList.value?.filter(param => 
      param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
    ) || []
    if (selectedParams.length === 0) {
      discreteAreaHeight.value = 0
    } // 如果没有选中参数，高度为0
    
    // 计算有多少个显示为离散模式的参数及通道
    let totalDiscreteChannels = 0
    const discreteParamCount = selectedParams.filter(name => {
      // 检查此参数的离散通道数量
      const discreteChannels = ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].filter(channelId => {
        return getChannelSettingDisplayMode(name, channelId) === 'discrete' && 
              getChannelSettingVisible(name, channelId)
      })
      
      // 累计通道数
      totalDiscreteChannels += discreteChannels.length
      
      // 如果该参数有任何通道显示为离散，则计入参数数
      return discreteChannels.length > 0
    }).length
    
    if (discreteParamCount === 0 || totalDiscreteChannels === 0) {
      discreteAreaHeight.value = 0
    } // 如果没有离散参数或通道，高度为0
    
    // 计算应该的高度：每个通道固定高度 * 通道总数 + 参数间间距
    const paramSpacing = 0 // 参数之间的间距
    const calculatedHeight = baseChannelHeight * totalDiscreteChannels + (discreteParamCount - 1) * paramSpacing
    
    // 限制最大高度
    discreteAreaHeight.value = Math.min(calculatedHeight, maxDiscreteAreaHeight)
  }
  // 获取当前的x轴范围
  const currentXDomain = xScale.domain();
  const visibleRange = (currentXDomain[1] as Date).getTime() - (currentXDomain[0] as Date).getTime()

  // 保存当前光标的相对位置比例，确保缩放后光标还在屏幕上相同的相对位置
  let cursorRelativePosition = 0.5; // 默认在中间
  if (currentCursorPosition.value !== null) {
    // 计算当前光标在可视区域中的相对位置

    // RJ模式：安全地处理各种时间格式
    let cursorTime: number;
    
    if (currentCursorPosition.value && typeof currentCursorPosition.value === 'object' && (currentCursorPosition.value as any).getTime) {
      // 如果是Date对象
      cursorTime = (currentCursorPosition.value as Date).getTime();
    } else if (typeof currentCursorPosition.value === 'number') {
      // 如果是时间戳数字，判断是秒还是毫秒
      if (currentCursorPosition.value < 10000000000) {
        // 秒时间戳，转换为毫秒
        cursorTime = currentCursorPosition.value * 1000;
      } else {
        // 毫秒时间戳
        cursorTime = currentCursorPosition.value;
      }
    } else if (typeof currentCursorPosition.value === 'string') {
      // 如果是时间字符串
      cursorTime = new Date(currentCursorPosition.value).getTime();
    } else {
      // 其他情况，使用当前时间
      cursorTime = Date.now();
    }
    
    const domainStart = (currentXDomain[0] as Date).getTime();
    cursorRelativePosition = (cursorTime - domainStart) / visibleRange;
  
    // 确保相对位置在0-1之间
    cursorRelativePosition = Math.max(0, Math.min(1, cursorRelativePosition));
    console.log('保存光标相对位置:', cursorRelativePosition);
  }

  // 清除现有线条和Y轴
  svg.selectAll('.line').remove();
  svg.selectAll('.param-y-axis').remove();
  
  // 隐藏初始化的y轴
  svg.select('.initial-y-axis').style('display', 'none');
  
  // 检查并更新裁剪区域
  updateClipPath(width, height);

  // 获取选中的参数列表
  const selectedParams = selectedParametersList.value?.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  ) || [];

  // 创建一个包含所有线条的组，并应用裁剪
  const linesGroup = svg.append('g')
    .attr('class', 'lines-group')
    .attr('clip-path', 'url(#chart-area)');

  // 为每个参数创建新的比例尺字典
  const currentParamYScales: { [paramName: string]: d3.ScaleLinear<number, number> } = {};

  // 为每个参数创建独立的Y轴
  selectedParams.forEach(paramName => {
    if (getParameterDisplayMode(paramName) == 'continuous') {
      // 获取矩形缩放或垂直缩放时保存的Y轴范围
      if (isRectZoomActive.value && rectZoomYRanges.value && rectZoomYRanges.value[paramName]) {
        const yRange = rectZoomYRanges.value[paramName];
        console.log(`使用参数 ${paramName} 的缩放Y轴范围:`, yRange);
        
        // 创建参数专用的Y轴比例尺
        const paramYScale = d3.scaleLinear()
          .domain(yRange)
          .range([height - margin.bottom, margin.top]);
        
        // 保存到当前函数的比例尺字典中并更新全局Y轴比例尺映射
        currentParamYScales[paramName] = paramYScale;
        paramYScaleMap.value[paramName] = paramYScale; // 这个很重要，确保后续缩放使用更新后的比例尺
        
        // 为该参数添加Y轴
        const yAxis = d3.axisLeft(paramYScale).ticks(5).tickFormat(formatAxisNumber);
        const escapedParamName = escapeSelector(paramName);
        const yAxisGroup = svg.append('g')
          .attr('class', `param-y-axis param-${escapedParamName}`)
          .attr('transform', `translate(${margin.left},0)`)
          .style('opacity', 0) // 默认设置为不可见
          .call(yAxis);
        
        // 添加参数名称
        yAxisGroup.append('text')
          .attr('class', 'axis-title')
          .attr('transform', 'rotate(0)') // 取消旋转
          .attr('y', 15) // 将文本上移到坐标轴上方
          .attr('x', -40) // 稍微右移一点
          .attr('dy', '0') // 取消垂直偏移
          .attr('fill', isDarkMode.value ? '#cccccc' : '#333')
          .attr('font-size', '11px')
          .style('text-anchor', 'start') // 左对齐
          .text(getShowName(paramName));
      } else {
        // 如果没有缩放范围，使用默认Y轴范围
        // 计算参数的数据范围
        let minValue = Infinity;
        let maxValue = -Infinity;
        let hasData = false;
        
        defaultData.value.forEach(channel => {
          const channelId = channel.channelId;
          const paramData = channel.parameters[paramName];
          
          if (!paramData || !Array.isArray(paramData)) return;
          
          // 检查此参数在此通道的显示方式
          const displayMode = getChannelSettingDisplayMode(paramName, channelId);
          if (displayMode === 'discrete') return;
          
          // 检查通道可见性
          if (!getChannelSettingVisible(paramName, channelId)) return;
          
          // 获取当前x轴范围内的数据
          const xDomain = xScale.domain();
          const visibleData = paramData.filter(d => {
            // 将时间字符串转换为Date进行比较
            const dataTime = new Date(d.timestamp).getTime()
            const domainStart = (xDomain[0] as Date).getTime()
            const domainEnd = (xDomain[1] as Date).getTime()
            return dataTime >= domainStart && dataTime <= domainEnd
            
          })
          
          if (visibleData.length > 0) {
            // 如果value为NaN，则不参与计算
            const dataMin = Math.min(...visibleData.map(d => d.value === 'NaN' ? Infinity : d.value))
            const dataMax = Math.max(...visibleData.map(d => d.value === 'NaN' ? -Infinity : d.value))
            minValue = Math.min(minValue, dataMin);
            maxValue = Math.max(maxValue, dataMax);
            hasData = true;
          }
        });
        
        if (hasData && minValue !== Infinity && maxValue !== -Infinity) {
          const valueRange = maxValue - minValue;
          
          // 扩展范围，使曲线显示在图表中间
          const paddingFactor = 0.2; // 上下各预留1/10
          const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
          
          // 计算Y轴范围
          const yMin = minValue - padding;
          const yMax = maxValue + padding;
          
          // 创建参数专用的Y轴比例尺
          const paramYScale = d3.scaleLinear()
            .domain([yMin, yMax])
            .range([height - margin.bottom, margin.top]);
            
          // 保存到当前函数的比例尺字典中并更新全局Y轴比例尺映射
          currentParamYScales[paramName] = paramYScale;
          paramYScaleMap.value[paramName] = paramYScale; // 确保新参数的Y轴比例尺被正确保存
          
          // 为该参数添加Y轴
          const yAxis = d3.axisLeft(paramYScale).ticks(5).tickFormat(formatAxisNumber);
          const escapedParamName = escapeSelector(paramName);
          const yAxisGroup = svg.append('g')
            .attr('class', `param-y-axis param-${escapedParamName}`)
            .attr('transform', `translate(${margin.left},0)`)
            .style('opacity', 0) // 默认设置为不可见
            .call(yAxis);
          
          // 添加参数名称
          yAxisGroup.append('text')
            .attr('class', 'axis-title')
            .attr('transform', 'rotate(0)') // 取消旋转
            .attr('y', 15) // 将文本上移到坐标轴上方
            .attr('x', -40) // 稍微右移一点
            .attr('dy', '0') // 取消垂直偏移
            .attr('fill', isDarkMode.value ? '#cccccc' : '#333')
            .attr('font-size', '11px')
            .style('text-anchor', 'start') // 左对齐
            .text(getShowName(paramName));
        }
      }
    }
  });
  
  // 只在矩形缩放或垂直缩放模式下，显示最后一个连续参数的Y轴
  if (selectedParams.length > 0) {
    // 从后向前查找第一个有效的连续参数
    let lastContinuousParam = null;
    for (let i = selectedParams.length - 1; i >= 0; i--) {
      if (getParameterDisplayMode(selectedParams[i]) === 'continuous' && 
          currentParamYScales[selectedParams[i]]) {
        lastContinuousParam = selectedParams[i];
        break;
      }
    }
    
    // 如果找到连续参数，只显示它的Y轴
    if (lastContinuousParam) {
      // 先将所有Y轴设置为不可见
      svg.selectAll('.param-y-axis').style('opacity', 0);
      
      // 再显示最后一个连续参数的Y轴
      svg.select(`.param-${escapeSelector(lastContinuousParam)}`)
        .style('opacity', 1);
    }
  }
  
  // 为每个参数绘制所有通道的曲线
  selectedParams.forEach(paramName => {
    if (getParameterDisplayMode(paramName) == 'continuous') {
      // 获取当前参数的Y轴比例尺
      const paramYScale = currentParamYScales[paramName];
      
      if (!paramYScale) return; // 如果没有对应的Y轴则跳过
      
      // 绘制该参数的所有通道曲线
      defaultData.value.forEach(channel => {
        const channelId = channel.channelId;
        const paramData = channel.parameters[paramName];
        
        if (!paramData || !Array.isArray(paramData)) return;
        
        // 检查视图设置中的可见性
        if (!getChannelSettingVisible(paramName, channelId)) return;
        
        // 检查显示方式，如果设置为离散量则不绘制曲线
        const displayMode = getChannelSettingDisplayMode(paramName, channelId);
        if (displayMode === 'discrete') return;
        
        // 在RJ模式下扩展数据，将最后一个数据点延续到x轴右边界之后
        // 首先将所有数据转换为普通对象，避免响应式Proxy与普通对象混合
        let extendedData = paramData.map(point => ({
          timestamp: point.timestamp,
          value: point.value
        }));
        
        if (extendedData.length > 0) {
          const lastPoint = extendedData[extendedData.length - 1];
          const xDomain = xScale.domain();
          const maxTime = xDomain[1] as Date;
          const lastDataTime = new Date(lastPoint.timestamp as string);
          
          // 始终添加一个延续点，延续到最后数据点的下一秒，确保覆盖完整的最后一秒
          const extendTime = new Date(Math.max(maxTime.getTime(), lastDataTime.getTime() + 1000));
          // 加上8小时的时区偏移（中国时区UTC+8）
          const extendTimeWithOffset = new Date(extendTime.getTime() + 8 * 60 * 60 * 1000);
          // 创建延续点，保持数据结构一致性
          const extendedPoint = {
            timestamp: extendTimeWithOffset.toISOString().replace('T', ' ').replace('Z', '').substring(0, 19),
            value: lastPoint.value
          };
          extendedData.push(extendedPoint);
        }

        // 类型为d3线条生成器
        const line = d3.line<{ timestamp: number | string; value: number }>()
          .x(d => {
            // RJ模式：将时间字符串转换为Date对象
            return xScale(new Date(d.timestamp as string))
          })
          .y(d => paramYScale(d.value))
          .curve(getLineType())
          .defined(d => {
            // 过滤掉值为NaN或无效的数据点
            // RJ模式：检查时间戳和值是否有效
            const timestamp = new Date(d.timestamp as string);
            return !isNaN(timestamp.getTime()) && !isNaN(d.value) && isFinite(d.value);
          });

        // 获取线条粗细
        const lineWidth = getChannelSettingLineWidth(paramName, channelId);
        
        // 创建路径并附加到线条组
        linesGroup.append('path')
          .datum(extendedData)
          .attr('class', `line line-${escapeSelector(paramName)}-${channelId}`)
          .attr('fill', 'none')
          .attr('stroke', getChannelColor(channelId, paramName))
          .attr('stroke-width', lineWidth)
          .attr('d', line)
          .style('pointer-events', 'none'); // 添加这一行，禁用曲线对鼠标事件的响应，允许事件传递到底层
      });
    }
  });
  
  // 更新离散量区域
  updateDiscreteArea();
  
  // 更新缩放带位置
  updateZoomBarPosition();
  
  // 重要：先删除现有的游标相关元素
  svg.selectAll('.cursor-line').remove();
  svg.selectAll('.cursor-group').remove();
  svg.selectAll('.cursor-points-group').remove();
  svg.selectAll('.measurement-cursor-group').remove();
  svg.selectAll('.discrete-cursor-line').remove();
  
  // 移除可滚动容器中的游标线
  const discreteScrollContainer = document.querySelector('.discrete-scroll-container');
  if (discreteScrollContainer) {
    const innerSvg = discreteScrollContainer.querySelector('.discrete-inner-svg');
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.discrete-cursor-line').remove();
    }
  }
  
  // 确保游标线在更新图表后重新显示
  if (currentCursorPosition.value !== null && interactionMode.value != 'measure') {
    // 计算新的光标位置，确保它在可见区域内
    const newXDomain = xScale.domain();
    const newVisibleRange = (newXDomain[1] as Date).getTime() - (newXDomain[0] as Date).getTime()
    
    // 使用保存的相对位置重新计算光标位置
    const newCursorPosition = new Date((newXDomain[0] as Date).getTime() + (cursorRelativePosition * newVisibleRange))
    console.log('新计算的光标位置:', newCursorPosition, '相对位置:', cursorRelativePosition);
    
    // 更新光标位置
    currentCursorPosition.value = newCursorPosition;
    const x = xScale(newCursorPosition);
    
    // 在更新游标位置前先确定是否有足够的图表区域高度
    if (height > margin.top + margin.bottom) {
      console.log('重绘游标线，位置:', x, newCursorPosition);
      
      // 更新游标位置
      updateCursorPosition(x, newCursorPosition);
      
      // 更新游标点
      updateCursorPoints(newCursorPosition);
    }
  }
  
  // 如果在测量模式下，同时更新测量游标
  if (interactionMode.value === 'measure' && isMeasuring.value) {
    // 确保测量游标也在可见区域内
    const newXDomain = xScale.domain();
    const newVisibleRange = (newXDomain[1] as Date).getTime() - (newXDomain[0] as Date).getTime()
    
    if (cursor1Position.value !== null) {
      // 重新计算测量开始点的位置，确保在可见区域内
      let startCursorRelativePos = 0;
      
        // RJ模式：安全地处理各种时间格式
        let cursor1Time: number;
        
        if (cursor1Position.value && typeof cursor1Position.value === 'object' && (cursor1Position.value as any).getTime) {
          cursor1Time = (cursor1Position.value as Date).getTime();
        } else if (typeof cursor1Position.value === 'number') {
          cursor1Time = cursor1Position.value < 10000000000 ? cursor1Position.value * 1000 : cursor1Position.value;
        } else if (typeof cursor1Position.value === 'string') {
          cursor1Time = new Date(cursor1Position.value).getTime();
        } else {
          cursor1Time = Date.now();
        }
        
        startCursorRelativePos = Math.max(0, Math.min(1, (cursor1Time - (newXDomain[0] as Date).getTime()) / newVisibleRange));
      
      const newStartPosition = new Date((newXDomain[0] as Date).getTime() + (startCursorRelativePos * newVisibleRange))
      cursor1Position.value = newStartPosition;
    }
    
    if (cursor2Position.value !== null) {
      // 重新计算测量结束点的位置，确保在可见区域内
      let endCursorRelativePos = 0;
      
      // RJ模式：安全地处理各种时间格式
      let cursor2Time: number;
      
      if (cursor2Position.value && typeof cursor2Position.value === 'object' && (cursor2Position.value as any).getTime) {
        cursor2Time = (cursor2Position.value as Date).getTime();
      } else if (typeof cursor2Position.value === 'number') {
        cursor2Time = cursor2Position.value < 10000000000 ? cursor2Position.value * 1000 : cursor2Position.value;
      } else if (typeof cursor2Position.value === 'string') {
        cursor2Time = new Date(cursor2Position.value).getTime();
      } else {
        cursor2Time = Date.now();
      }
      
      endCursorRelativePos = Math.max(0, Math.min(1, (cursor2Time - (newXDomain[0] as Date).getTime()) / newVisibleRange));
      
      const newEndPosition = new Date((newXDomain[0] as Date).getTime() + (endCursorRelativePos * newVisibleRange))
      cursor2Position.value = newEndPosition;
    }
    
    updateMeasurementCursors();
    updateMeasurementResults();
  }
  
  // 如果故障模式激活且有选中的故障，重新绘制故障线
  if (isFaultModeActive.value && currentSelectedFaultIds.value && currentSelectedFaultIds.value.length > 0 && currentFaultList.value && !updateFaultArea.value) {
    updateFaultDisplayMode('', currentSelectedFaultIds.value, currentFaultList.value)
    updateFaultArea.value = false
  } else {
    updateFaultArea.value = false
  }

  // 重新绘制后更新标注高亮，确保高亮能正确显示
  if (annotationHighlights.value.length > 0) {
    updateAnnotationHighlights()
  }
}



// 切换主题函数
// const {ipcRederer} = window.require('electorn')
const toggleTheme = () => {
  isDarkMode.value = !isDarkMode.value;
  document.documentElement.setAttribute("dark-mode", isDarkMode.value);
  localStorage.setItem('dark-mode', isDarkMode.value);

  emit('update:darkMode', isDarkMode.value)
  
  // 延迟更新图表，以确保DOM样式已更新
  nextTick(() => {
    applyThemeColors()
    updateAxes()
    updateZoomBarPosition()
    updateDiscreteArea()
    // 检查当前模式并更新相应的游标
    if (interactionMode.value === 'measure' && isMeasuring.value) {
      // 如果在测量模式下，更新测量游标
      updateMeasurementCursors()
      updateMeasurementResults()
    } else if (currentCursorPosition.value !== null) {
      // 普通游标模式
      const x = xScale(currentCursorPosition.value)
      updateCursorPosition(x, currentCursorPosition.value)
    }
    
    // 更新游标标签
    updateCursorLabels()
  })
}

// 工具下拉菜单相关函数
const toggleToolsDropdown = () => {
  showToolsDropdown.value = !showToolsDropdown.value
}

const handleVirtualParam = () => {
  showToolsDropdown.value = false
  // 触发虚拟参数事件
  emit('open-virtual-param')
}

const handleFaultManage = () => {
  showToolsDropdown.value = false
  // 触发故障管理事件
  isFaultModeActive.value = !isFaultModeActive.value
  emit('toggle-fault-mode', isFaultModeActive.value)
}

const changeFaultMode = (isActive: boolean) => {
  isFaultModeActive.value = isActive
  // if (!isActive) {
  //   // 清除故障线
  //   if (svg) {
  //     svg.selectAll('.fault-line').remove()
  //     svg.selectAll('.fault-bubble').remove()
  //   }
  //   // 清空故障状态
  //   currentSelectedFaultIds.value = []
  //   currentFaultList.value = []
  // }
}

const handleExportData = () => {
  showToolsDropdown.value = false
  // 打开导出数据模态框
  emit('open-export-data')
}

const handleSimilaritySearch = () => {
  showToolsDropdown.value = false
  // 调用相似性检索
  emit('open-similarity-search')
}

// 监听点击外部关闭下拉菜单
onMounted(() => {
  const handleClickOutside = (event: Event) => {
    if (toolsDropdown.value && !toolsDropdown.value.contains(event.target as Node)) {
      showToolsDropdown.value = false
    }
  }
  document.addEventListener('click', handleClickOutside)
  
  onUnmounted(() => {
    document.removeEventListener('click', handleClickOutside)
  })
})

// 持久化存储用户拖动的标签偏移量
const labelOffsets = ref<Map<string, { offsetX: number, offsetY: number }>>(new Map())

// 清除所有游标标签
const clearCursorLabels = () => {
  if (!svg) return
  svg.selectAll('.cursor-label-group').remove()
  cursorLabels.value = []
}

// 更新游标标签
const updateCursorLabels = () => {
  if (!svg || !chartContainer.value) return
  
  // 清除现有标签
  clearCursorLabels()
  
  // 获取当前游标位置
  let currentTimestamp: number | Date
  let cursorX: number
  
  if (interactionMode.value === 'measure' && isMeasuring.value) {
    // 测量模式：为两个游标都创建标签
    const timestamps = [cursor1Position.value, cursor2Position.value]
    const cursors = ['cursor1', 'cursor2']
    
    for (let i = 0; i < timestamps.length; i++) {
      currentTimestamp = timestamps[i]
      cursorX = xScale(currentTimestamp as any)
      createLabelsForCursor(currentTimestamp, cursorX, cursors[i])
    }
  } else {
    // 普通模式：为当前游标创建标签
    currentTimestamp = currentCursorPosition.value
    if (currentTimestamp === null || currentTimestamp === undefined) return
    
    cursorX = xScale(currentTimestamp)
    createLabelsForCursor(currentTimestamp, cursorX, 'normal')
  }
}

// 为指定游标创建标签
const createLabelsForCursor = (timestamp: number | Date, cursorX: number, cursorType: string) => {
  if (!svg || !selectedParametersList.value) return
  // 获取选中的参数列表
  const selectedParams = selectedParametersList.value.filter(param => 
    param !== 'RecTime' || (param === 'RecTime' && selectedParametersListInView.value.includes('RecTime'))
  )
  
  if (selectedParams.length === 0) return
  
  // 获取当前游标数据
  const cursorData = getCursorData(timestamp)
  const labels: CursorLabel[] = []
  
  // 为每个参数的每个通道创建标签
  selectedParams.forEach(paramName => {
    // 首先检查该参数是否设置为显示标签
    if (!getParameterShowLabel(paramName)) return
    
    ['leftA', 'leftB', 'rightA', 'rightB', 'RJ'].forEach(channelId => {
      // 检查该通道是否显示为连续量且可见
      if (getChannelSettingDisplayMode(paramName, channelId) === 'continuous' && 
          getChannelSettingVisible(paramName, channelId)) {
        // 检查是否有该参数的数据
        if (cursorData[channelId] && cursorData[channelId][paramName]) {
          const dataPoint = cursorData[channelId][paramName]
          const value = dataPoint.value
          
          // 获取参数对应的Y轴比例尺
          const paramYScale = getParamYScale(paramName)
          if (!paramYScale) return
          
          const cursorY = paramYScale(value)
          
          // 获取通道颜色
          const color = getChannelColor(channelId, paramName)

          
          // 生成标签文本（包含数值）
          const channelText = getChannelDisplayName(channelId)
          const formattedValue = typeof value === 'number' ? value.toFixed(3) : value
          const labelText = `${getShowName(paramName)}${channelText}: ${formattedValue}`
          
          // 计算标签位置（右上角偏移）
          const labelId = `${cursorType}-${paramName}-${channelId}`
          const baseOffsetX = 8
          const baseOffsetY = -8
          
          // 从持久化存储中获取用户拖动的偏移量
          const savedOffset = labelOffsets.value.get(labelId)
          const offsetX = savedOffset ? savedOffset.offsetX : baseOffsetX
          const offsetY = savedOffset ? savedOffset.offsetY : baseOffsetY
          
          const label: CursorLabel = {
            id: labelId,
            paramName,
            channelId,
            x: cursorX + offsetX,
            y: cursorY + offsetY,
            color,
            text: labelText,
            isDragging: false,
            offsetX, // 用户拖动的偏移量
            offsetY  // 用户拖动的偏移量
          }
          
          labels.push(label)
        }
      }
    })
  })
  // 处理标签重叠
  adjustLabelsForOverlap(labels)
  
  // 确保所有标签都在画布边界内
  labels.forEach(label => keepLabelInBounds(label, cursorX))
  
  // 渲染标签
  renderCursorLabels(labels, cursorX)
  
  // 保存标签到状态
  cursorLabels.value.push(...labels)
}

// 获取通道显示名称
const getChannelDisplayName = (channelId: string): string => {
  switch (channelId) {
    case 'leftA': return '-左-A'
    case 'leftB': return '-左-B'
    case 'rightA': return '-右-A'
    case 'rightB': return '-右-B'
    case 'RJ': return ''
    default: return `-${channelId}`
  }
}

// 确保标签在画布边界内
const keepLabelInBounds = (label: CursorLabel, cursorX: number): void => {
  if (!chartContainer.value) return
  
  const containerWidth = chartContainer.value.clientWidth
  const containerHeight = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 估算标签宽度（基于文本长度）
  const estimatedLabelWidth = label.text.length * 8  // 大概估算
  const labelHeight = 16
  
  // 检查水平边界
  if (label.x + estimatedLabelWidth > containerWidth - margin.right) {
    // 超出右边界，优先调整到游标左侧
    const leftPosition = cursorX - estimatedLabelWidth - 8
    if (leftPosition >= margin.left) {
      label.x = leftPosition
    } else {
      // 如果左侧也放不下，贴着右边界
      label.x = containerWidth - margin.right - estimatedLabelWidth
    }
    label.offsetX = label.x - cursorX
  } else if (label.x < margin.left) {
    // 超出左边界，调整到右侧
    label.x = margin.left + 8
    label.offsetX = label.x - cursorX
  }
  
  // 检查垂直边界
  if (label.y - labelHeight < margin.top) {
    // 超出上边界，调整向下
    label.y = margin.top + labelHeight + 4
    // 重新计算相对于数据点的偏移量
    const paramYScale = getParamYScale(label.paramName)
    if (paramYScale) {
      const cursorData = getCursorData(interactionMode.value === 'measure' ? 
        (activeCursor.value === 'cursor1' ? cursor1Position.value : cursor2Position.value) : 
        currentCursorPosition.value)
      
      if (cursorData[label.channelId] && cursorData[label.channelId][label.paramName]) {
        const dataValue = cursorData[label.channelId][label.paramName].value
        const dataPointY = paramYScale(dataValue)
        label.offsetY = label.y - dataPointY
      }
    }
  } else if (label.y > containerHeight - margin.bottom - 4) {
    // 超出下边界，调整向上
    label.y = containerHeight - margin.bottom - 4
    // 重新计算相对于数据点的偏移量
    const paramYScale = getParamYScale(label.paramName)
    if (paramYScale) {
      const cursorData = getCursorData(interactionMode.value === 'measure' ? 
        (activeCursor.value === 'cursor1' ? cursor1Position.value : cursor2Position.value) : 
        currentCursorPosition.value)
      
      if (cursorData[label.channelId] && cursorData[label.channelId][label.paramName]) {
        const dataValue = cursorData[label.channelId][label.paramName].value
        const dataPointY = paramYScale(dataValue)
        label.offsetY = label.y - dataPointY
      }
    }
  }
}

// 调整标签位置以避免重叠
const adjustLabelsForOverlap = (labels: CursorLabel[]) => {
  // 按Y坐标排序
  labels.sort((a, b) => a.y - b.y)
  
  const labelHeight = 16 // 假设标签高度为16px
  const minSpacing = 4 // 标签间最小间距
  
  for (let i = 1; i < labels.length; i++) {
    const currentLabel = labels[i]
    const prevLabel = labels[i - 1]
    
    // 检查是否重叠
    if (currentLabel.y - prevLabel.y < labelHeight + minSpacing) {
      // 计算需要调整的距离
      const adjustment = (prevLabel.y + labelHeight + minSpacing) - currentLabel.y
      // 只调整显示位置，不修改用户拖动的原始偏移量
      currentLabel.y += adjustment
      // 注意：这里不修改 offsetY，保持用户拖动的原始偏移量
    }
  }
}

// 渲染游标标签
const renderCursorLabels = (labels: CursorLabel[], cursorX: number) => {
  if (!svg) return
  
  // 创建标签组
  const labelGroup = svg.append('g')
    .attr('class', 'cursor-label-group')
    .attr('clip-path', 'url(#chart-area)')
  
  labels.forEach(label => {
    // 创建标签背景
    const labelBg = labelGroup.append('rect')
      .attr('class', 'cursor-label-bg')
      .attr('x', label.x - 2)
      .attr('y', label.y - 12)
      .attr('rx', 3)
      .attr('ry', 3)
      .attr('fill', isDarkMode.value ? 'rgba(30, 30, 30, 0.8)' : 'rgba(255, 255, 255, 0.8)') // 明亮模式浅透明色，黑夜模式深透明色
      .attr('stroke', 'none')
      .attr('stroke-width', 0)
      .style('cursor', 'move')
    
    // 创建标签文本
    const labelText = labelGroup.append('text')
      .attr('class', 'cursor-label-text')
      .attr('x', label.x)
      .attr('y', label.y - 2)
      .attr('fill', label.color)
      .attr('font-weight', 'bold')
      .text(label.text)
      .style('cursor', 'move')
      .style('user-select', 'none')
      .style('pointer-events', 'none')
    
    // 计算文本宽度并调整背景大小
    const textBBox = (labelText.node() as SVGTextElement).getBBox()
    labelBg
      .attr('width', textBBox.width + 4)
      .attr('height', textBBox.height + 2)
    
    // 添加拖动功能
    let startX = 0
    let startY = 0
    
    labelBg
      .on('mousedown', function(event) {
        event.stopPropagation()
        isDraggingLabel.value = true
        draggingLabelId.value = label.id
        startX = event.offsetX
        startY = event.offsetY
        
        // 添加全局鼠标移动和释放事件
        const handleMouseMove = (e: MouseEvent) => {
          if (isDraggingLabel.value && draggingLabelId.value === label.id) {
            const deltaX = e.offsetX - startX
            const deltaY = e.offsetY - startY
            
            // 更新用户拖动的偏移量（相对于数据点的偏移）
            label.offsetX += deltaX
            label.offsetY += deltaY
            
            // 直接更新绝对位置
            label.x += deltaX
            label.y += deltaY
            
            // 确保标签在边界内
            const cursorX = xScale(interactionMode.value === 'measure' ? 
              (activeCursor.value === 'cursor1' ? cursor1Position.value : cursor2Position.value) : 
              currentCursorPosition.value)
            keepLabelInBounds(label, cursorX)
            
            // 更新DOM元素位置
            labelBg
              .attr('x', label.x - 2)
              .attr('y', label.y - 12)
            
            labelText
              .attr('x', label.x)
              .attr('y', label.y - 2)
            
            // 更新cursorLabels.value中对应标签的偏移量
            const existingLabelIndex = cursorLabels.value.findIndex(l => l.id === label.id)
            if (existingLabelIndex >= 0) {
              cursorLabels.value[existingLabelIndex].offsetX = label.offsetX
              cursorLabels.value[existingLabelIndex].offsetY = label.offsetY
            }
            
            // 保存用户拖动的偏移量到持久化存储
            labelOffsets.value.set(label.id, {
              offsetX: label.offsetX,
              offsetY: label.offsetY
            })
            
            startX = e.offsetX
            startY = e.offsetY
          }
        }
        
        const handleMouseUp = () => {
          isDraggingLabel.value = false
          draggingLabelId.value = null
          window.removeEventListener('mousemove', handleMouseMove)
          window.removeEventListener('mouseup', handleMouseUp)
        }
        
        window.addEventListener('mousemove', handleMouseMove)
        window.addEventListener('mouseup', handleMouseUp)
      })
  })
}

// 获取参数的Y轴比例尺
const getParamYScale = (paramName: string): d3.ScaleLinear<number, number> | null => {
  // 首先尝试从paramYScaleMap中获取已存储的Y轴比例尺
  if (paramYScaleMap.value[paramName]) {
    return paramYScaleMap.value[paramName]
  }
  
  // 如果paramYScaleMap中没有，则尝试根据当前状态创建比例尺
  if (!chartContainer.value) return null
  
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 如果在矩形缩放模式下，使用矩形缩放的Y轴范围
  if (isRectZoomActive.value && rectZoomYRanges.value && rectZoomYRanges.value[paramName]) {
    const yRange = rectZoomYRanges.value[paramName]
    return d3.scaleLinear()
      .domain(yRange)
      .range([height - margin.bottom, margin.top])
  }
  
  // 检查用户是否设置了Y轴范围
  const userYAxisMin = getParameterYAxisMin(paramName)
  const userYAxisMax = getParameterYAxisMax(paramName)
  
  if (userYAxisMin !== undefined || userYAxisMax !== undefined) {
    // 如果用户设置了Y轴范围，需要计算实际的数据范围来补充未设置的部分
    let computedMin = Infinity
    let computedMax = -Infinity
    let hasData = false
    
    // 计算该参数在所有可见通道中的数据范围
    const channelIds = ['leftA', 'leftB', 'rightA', 'rightB']
    channelIds.forEach(channelId => {
      if (!getChannelSettingVisible(paramName, channelId)) return
      
      const channelData = props.chartData?.[channelId]?.[paramName]
      if (channelData && Array.isArray(channelData)) {
        channelData.forEach(point => {
          if (typeof point.value === 'number') {
            computedMin = Math.min(computedMin, point.value)
            computedMax = Math.max(computedMax, point.value)
            hasData = true
          }
        })
      }
    })
    
    // 如果有数据，扩展范围
    if (hasData) {
      const valueRange = computedMax - computedMin
      const paddingFactor = 0.2
      const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
      computedMin -= padding / 2
      computedMax += padding / 2
    }
    
    const finalMin = userYAxisMin !== undefined ? userYAxisMin : computedMin
    const finalMax = userYAxisMax !== undefined ? userYAxisMax : computedMax
    
    return d3.scaleLinear()
      .domain([finalMin, finalMax])
      .range([height - margin.bottom, margin.top])
  }
  
  // 如果共享Y轴模式，返回全局Y轴比例尺
  if (useSharedYAxis.value && yScale) {
    return yScale
  }
  
  // 最后的回退方案：计算该参数的自动范围
  let minValue = Infinity
  let maxValue = -Infinity
  let hasData = false
  
  const channelIds = ['leftA', 'leftB', 'rightA', 'rightB']
  channelIds.forEach(channelId => {
    if (!getChannelSettingVisible(paramName, channelId)) return
    
    const channelData = props.chartData?.[channelId]?.[paramName]
    if (channelData && Array.isArray(channelData)) {
      channelData.forEach(point => {
        if (typeof point.value === 'number') {
          minValue = Math.min(minValue, point.value)
          maxValue = Math.max(maxValue, point.value)
          hasData = true
        }
      })
    }
  })
  
  if (hasData && minValue !== Infinity && maxValue !== -Infinity) {
    const valueRange = maxValue - minValue
    const paddingFactor = 0.2
    const padding = valueRange != 0 ? valueRange * paddingFactor : 1;
    
    const yMin = minValue - padding / 2
    const yMax = maxValue + padding / 2
    
    return d3.scaleLinear()
      .domain([yMin, yMax])
      .range([height - margin.bottom, margin.top])
  }
  
  // 如果没有数据，返回null
  return null
}

// 应用主题颜色到SVG元素
const applyThemeColors = () => {
  if (!svg) return
  
  const axisColor = isDarkMode.value ? '#aaaaaa' : '#000000'
  const gridColor = isDarkMode.value ? '#333333' : '#f0f0f0'
  const axisTextColor = isDarkMode.value ? '#cccccc' : '#333333'
  const cursorLineColor = isDarkMode.value ? '#fff' : '#1890ff'
  
  // 更新坐标轴颜色
  svg.selectAll('.axis path, .axis line').attr('stroke', axisColor)
  svg.selectAll('.axis text').attr('fill', axisColor)
  
  // 更新Y轴标题的颜色
  svg.selectAll('.axis-title').attr('fill', axisTextColor)
  
  // 更新游标线的颜色
  svg.selectAll('.cursor-line').attr('stroke', cursorLineColor)
  svg.selectAll('.discrete-cursor-line').attr('stroke', cursorLineColor)
  
  // 更新测量模式下的游标线颜色
  svg.selectAll('.cursor-line-1, .cursor-line-2').attr('stroke', cursorLineColor)
  
  // 更新游标手柄颜色
  const handleFill = isDarkMode.value ? '#3b82f6' : '#3b82f6'
  const handleStroke = isDarkMode.value ? '#1e40af' : '#1e40af'
  svg.selectAll('.cursor-handle')
    .attr('fill', handleFill)
    .attr('stroke', handleStroke)
  
  // 更新网格线颜色
  svg.selectAll('.grid line').attr('stroke', gridColor)
  
  // 更新缩放带背景色
  svg.select('.zoom-bar-background')
    .attr('fill', isDarkMode.value ? '#1e1e1e' : '#f5f5f5')
    .attr('stroke', isDarkMode.value ? '#444444' : '#e0e0e0')
    
  // 更新离散区域背景色
  svg.select('.discrete-background')
    .attr('fill', isDarkMode.value ? '#1e1e1e' : '#f5f5f5')
    .attr('stroke', isDarkMode.value ? '#444444' : '#e0e0e0')
}

// 添加图表区域的点击事件处理
const handleChartClick = (event: MouseEvent) => {
  // 设置焦点
  setFocus()
  
  // 处理图表区域的点击
  if (!chartContainer.value || !svg) return
  
  const x = event.offsetX
  const y = event.offsetY
  
  // 确保只在主图表区域内响应鼠标事件
  if (y > chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value - margin.bottom) {
    return // 忽略离散量区域和缩放带上的事件
  }
  
  // 多边形模式下添加点
  if (interactionMode.value === 'polygon') {
    addPointToPolygon(x, y)
  }
}

// 添加一个辅助函数，用于将参数ID转换为安全的CSS类名
// 将特殊字符（包括空格）替换为下划线，确保类名有效
const escapeSelector = (selector: string): string => {
  // 将空格和特殊字符替换为下划线，确保CSS类名有效
  // CSS类名不能包含空格，空格用于分隔多个类名
  return selector.replace(/[!"#$%&'()*+,\/.:;<=>?@^`[\\\]{|}~ ]/g, '_')
}

// 绘制故障线函数
const drawFaultLines = (selectedFaultIds: string[], faultList: any[]) => {
  if (!svg || !xScale || !chartContainer.value) return
  
  // 更新当前故障状态
  currentSelectedFaultIds.value = selectedFaultIds
  currentFaultList.value = faultList
  
  // 获取图表尺寸
  const width = chartContainer.value.clientWidth
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - faultBandsAreaHeight.value - zoomBarHeight.value
  
  // 清除之前的故障线和气泡
  svg.selectAll('.fault-line').remove()
  svg.selectAll('.fault-bubble').remove()
  
  // 如果没有选中的故障，直接返回
  if (!selectedFaultIds || selectedFaultIds.length === 0) return
  
  // 获取选中的故障
  const selectedFaults = faultList.filter(fault => selectedFaultIds.includes(fault.id))
  
  // 收集所有故障点，用于处理重叠
  const faultPoints: Array<{
    xPos: number,
    fault: any,
    occurrence: any
  }> = []
  
  // 收集所有故障点
  selectedFaults.forEach(fault => {
    fault.occurrences.forEach((occurrence: any) => {
      let timestamp = occurrence.startTime
      let level = occurrence.alarmLevel
      
      // 根据当前模式处理时间戳
      // RJ模式：确保时间戳是Date类型
      if (typeof timestamp === 'string') {
        timestamp = new Date(timestamp)
      } else if (typeof timestamp === 'number') {
        timestamp = new Date(timestamp)
      }
      
      const xPos = xScale(timestamp)
      
      // 检查位置是否在可见范围内
      if (xPos >= margin.left && xPos <= width - margin.right) {
        faultPoints.push({ xPos, fault, occurrence, level })
      }
    })
  })
  
  // 按 x 位置排序
  faultPoints.sort((a, b) => a.xPos - b.xPos)
  
  // 计算可用的标签显示区域
  const labelAreaTop = margin.top + 30  // 标签区域顶部
  const labelAreaHeight = height - margin.bottom - labelAreaTop - 20  // 可用高度，留20px缓冲
  // const maxLabels = Math.max(1, Math.floor(labelAreaHeight / 35))  // 最多能显示的标签数量
  const maxLabels = 5
  
  // 为每个故障点绘制红色竖线和标签
  faultPoints.forEach((point, index) => {
    const { xPos, fault, level } = point
    
    // 绘制红色竖线
    svg.append('line')
      .attr('class', 'fault-line')
      .attr('x1', xPos)
      .attr('y1', margin.top + 30)
      .attr('x2', xPos)
      .attr('y2', height - margin.bottom)
      .attr('stroke', level == '0' ? '#dc2626' : level == '1' ? '#facc15' : '#3b82f6')
      .attr('stroke-width', 1)
      .style('pointer-events', 'none')
    
    // 计算标签的垂直偏移，避免重叠
    // 收集所有在200px范围内的故障点
    const nearbyPoints = faultPoints.filter(p => 
      Math.abs(p.xPos - xPos) <= 200
    )
    
    // 按x位置排序，然后按故障名称排序确保一致性
    nearbyPoints.sort((a, b) => {
      if (Math.abs(a.xPos - b.xPos) < 1) {
        // 如果x位置相同（差异小于1px），按故障名称排序
        return a.fault.name.localeCompare(b.fault.name)
      }
      return a.xPos - b.xPos
    })
    
    // 找到当前故障点在排序后数组中的位置
    const pointIndex = nearbyPoints.findIndex(p => 
      Math.abs(p.xPos - xPos) < 1 && p.fault.id === fault.id
    )
    
    // 限制垂直偏移，确保不超出可显示区域
    const limitedIndex = Math.min(pointIndex, maxLabels - 1)
    const verticalOffset = limitedIndex * 35 // 每个标签间隔35px
    
    // 检查是否需要显示省略指示器
    const hasMoreLabels = nearbyPoints.length > maxLabels && pointIndex >= maxLabels
    
    // 如果当前标签超出显示范围，跳过绘制或显示省略信息
    if (pointIndex >= maxLabels) {
      // 只在第一个超出的位置显示省略指示器
      if (pointIndex === maxLabels) {
        const ellipsisGroup = svg.append('g')
          .attr('class', 'fault-bubble')
          .attr('transform', `translate(${xPos}, ${labelAreaTop + (maxLabels - 1) * 35 + 35})`)
        
        // 省略指示器（灰色三角形）
        ellipsisGroup.append('polygon')
          .attr('points', '0,0 -6,-8 6,-8')
          .attr('fill', '#9ca3af')
          .attr('stroke', '#6b7280')
          .attr('stroke-width', 1)
        
        // 省略文本
        const moreCount = nearbyPoints.length - maxLabels
        ellipsisGroup.append('text')
          .attr('text-anchor', 'middle')
          .attr('dy', '-15')
          .attr('font-size', '10px')
          .attr('font-weight', 'normal')
          .attr('fill', '#6b7280')
          .text(`+${moreCount}个故障`)
          .style('pointer-events', 'none')
          .append('title')
          .text(`还有${moreCount}个故障未显示，请缩放图表查看详情`)
      }
      return // 跳过当前标签的绘制
    }
    
    // 创建标签组
    const labelGroup = svg.append('g')
      .attr('class', 'fault-bubble')
      .attr('transform', `translate(${xPos}, ${labelAreaTop + verticalOffset})`)
    
    // 箭头指向故障线（红色三角形）
    labelGroup.append('polygon')
      .attr('points', '0,0 -6,-8 6,-8')
      .attr('fill', level == '0' ? '#dc2626' : level == '1' ? '#facc15' : '#3b82f6')
      .attr('stroke', level == '0' ? '#dc2626' : level == '1' ? '#facc15' : '#3b82f6')
      .attr('stroke-width', 1)
    
    // 故障名称文本（红色，位于箭头上方）
    const faultText = fault.name
    const maxTextLength = 10 // 最大显示字符数
    const displayText = faultText.length > maxTextLength ? faultText.substring(0, maxTextLength) + '...' : faultText
    
    const textElement = labelGroup.append('text')
      .attr('text-anchor', 'middle')
      .attr('dy', '-15') // 位于箭头上方
      .attr('font-size', '12px')
      .attr('font-weight', 'bold')
      .attr('fill', level == '0' ? '#dc2626' : level == '1' ? '#facc15' : '#3b82f6') // 红色文字
      .text(displayText)
      .style('pointer-events', 'auto')
    
    // 如果文本被省略，添加title属性用于悬浮显示完整名称
    if (faultText.length > maxTextLength) {
      textElement.append('title').text(faultText)
    }
  })
}

// 绘制故障色带函数
const drawFaultBands = (selectedFaultIds: string[], faultList: any[]) => {
  if (!svg || !xScale || !chartContainer.value) {
    console.warn('SVG, xScale, or chartContainer not available')
    return
  }
  // 清除现有的故障色带
  svg.selectAll('.fault-band').remove()

  // 获取容器尺寸
  const width = chartContainer.value.clientWidth

  // 计算故障色带的总高度
  const faultBandHeight = 15 // 每个故障色带的高度
  const faultBandSpacing = 0 // 故障色带之间的间距
  const totalFaultBandHeight = selectedFaultIds.length * faultBandHeight + (selectedFaultIds.length - 1) * faultBandSpacing + 10 // 10px为与开关量色带的间距

  // 计算主图表高度（减去离散量区域、故障色带区域和缩放带高度）
  const height = chartContainer.value.clientHeight - discreteAreaHeight.value - Math.min(totalFaultBandHeight, maxFaultBandsAreaHeight) - zoomBarHeight.value

  // 计算故障色带的位置：在开关量色带上方
  const faultBandStartY = height
  // 更新故障色带区域高度
  faultBandsAreaHeight.value = Math.min(totalFaultBandHeight, maxFaultBandsAreaHeight)

  // 移除可能存在的滚动容器
  const existingScrollContainer = document.querySelector('.fault-scroll-container')
  if (existingScrollContainer && existingScrollContainer.parentNode) {
    // 在移除前先清除内部SVG中的游标线
    const innerSvg = existingScrollContainer.querySelector('.fault-inner-svg')
    if (innerSvg) {
      d3.select(innerSvg).selectAll('.fault-cursor-line').remove()
    }
    
    existingScrollContainer.parentNode.removeChild(existingScrollContainer)
  }

  // 判断是否需要滚动显示
  const needScrolling = totalFaultBandHeight > maxFaultBandsAreaHeight

  if (needScrolling) {
    // 创建可滚动的容器
    const scrollContainer = document.createElement('div')
    scrollContainer.className = 'fault-scroll-container'
    scrollContainer.style.cssText = `
      position: absolute;
      left: 0;
      top: ${faultBandStartY}px;
      width: ${width}px;
      height: ${maxFaultBandsAreaHeight - 10}px;
      overflow-y: auto;
      overflow-x: hidden;
      z-index: 10;
      background: transparent;
    `

    // 创建内部SVG
    const innerSvg = d3.select(scrollContainer)
      .append('svg')
      .attr('class', 'fault-inner-svg')
      .attr('width', width)
      .attr('height', totalFaultBandHeight -10)
      .style('display', 'block')

    // 在内部SVG中绘制故障色带
    selectedFaultIds.forEach((faultId, index) => {
      const fault = faultList.find(f => f.id === faultId)
      if (!fault) return

      const bandY = index * (faultBandHeight + faultBandSpacing)

      // 创建故障色带组
      const bandGroup = innerSvg.append('g')
        .attr('class', 'fault-band')

      // 首先绘制整个范围的绿色背景（正常状态）
      bandGroup.append('rect')
        .attr('x', margin.left)
        .attr('y', bandY)
        .attr('width', width - margin.left - margin.right)
        .attr('height', faultBandHeight)
        .attr('fill', '#22c55e')
        .attr('opacity', 0.7)
        .append('title')
        .text(`${fault.name}: 正常期间`)

      // 然后在故障时间段绘制红色色带
      fault.occurrences.forEach((occurrence: any) => {
        const startTime = occurrence.startTime
        const endTime = occurrence.endTime

        // 处理时间戳格式
        let startTimestamp: number | Date
        let endTimestamp: number | Date

        // RJ模式下处理时间戳
        if (typeof startTime === 'string') {
          startTimestamp = new Date(startTime)
        } else if (startTime instanceof Date) {
          startTimestamp = startTime
        } else {
          startTimestamp = new Date(startTime * 1000)
        }

        if (typeof endTime === 'string') {
          endTimestamp = new Date(endTime)
        } else if (endTime instanceof Date) {
          endTimestamp = endTime
        } else {
          endTimestamp = new Date(endTime * 1000)
        }

        // 计算X坐标
        const x1 = Math.max(margin.left, xScale(startTimestamp))
        const x2 = Math.min(width - margin.right, xScale(endTimestamp))
        const bandWidth = x1 === x2 ? 1 : Math.max(0, x2 - x1)

        if (bandWidth > 0) {
          // 绘制故障期间的红色色带
          bandGroup.append('rect')
            .attr('x', x1)
            .attr('y', bandY)
            .attr('width', bandWidth)
            .attr('height', faultBandHeight)
            .attr('fill', occurrence.alarmLevel == '0' ? '#dc2626' : occurrence.alarmLevel == '1' ? '#facc15' : '#3b82f6') //根据等级确定颜色：严重红色，一般黄色，提醒蓝色
            .attr('opacity', 0.8)
            .append('title')
            .text(`${fault.name}: 故障期间 (${startTime} - ${endTime})`)
        }
      })

      // 添加故障名称标签（在色带左侧）
      bandGroup.append('text')
        .attr('x', margin.left + 5)
        .attr('y', bandY + faultBandHeight / 2)
        .attr('text-anchor', 'start')
        .attr('dominant-baseline', 'middle')
        .attr('font-size', '10px')
        .attr('font-weight', 'bold')
        .attr('fill', '#fff')
        .text(fault.name)
        .style('pointer-events', 'none')
    })

    // 将滚动容器添加到图表容器中
    chartContainer.value.appendChild(scrollContainer)

  } else {
    // 不需要滚动时，直接在主SVG中绘制
    selectedFaultIds.forEach((faultId, index) => {
      const fault = faultList.find(f => f.id === faultId)
      if (!fault) return

      const bandY = faultBandStartY + index * (faultBandHeight + faultBandSpacing)

      // 创建故障色带组
      const bandGroup = svg.append('g')
        .attr('class', 'fault-band')

      // 首先绘制整个范围的绿色背景（正常状态）
      bandGroup.append('rect')
        .attr('x', margin.left)
        .attr('y', bandY)
        .attr('width', width - margin.left - margin.right)
        .attr('height', faultBandHeight)
        .attr('fill', '#22c55e')
        .attr('opacity', 0.7)
        .append('title')
        .text(`${fault.name}: 正常期间`)

      // 然后在故障时间段绘制红色色带
      fault.occurrences.forEach((occurrence: any) => {
        const startTime = occurrence.startTime
        const endTime = occurrence.endTime

        // 处理时间戳格式
        let startTimestamp: number | Date
        let endTimestamp: number | Date

        // RJ模式下处理时间戳
        if (typeof startTime === 'string') {
          startTimestamp = new Date(startTime)
        } else if (startTime instanceof Date) {
          startTimestamp = startTime
        } else {
          startTimestamp = new Date(startTime * 1000)
        }

        if (typeof endTime === 'string') {
          endTimestamp = new Date(endTime)
        } else if (endTime instanceof Date) {
          endTimestamp = endTime
        } else {
          endTimestamp = new Date(endTime * 1000)
        }

        // 计算X坐标
        const x1 = Math.max(margin.left, xScale(startTimestamp))
        const x2 = Math.min(width - margin.right, xScale(endTimestamp))
        const bandWidth = x1 === x2 ? 1 : Math.max(0, x2 - x1)

        if (bandWidth > 0) {
          // 绘制故障期间的红色色带
          bandGroup.append('rect')
            .attr('x', x1)
            .attr('y', bandY)
            .attr('width', bandWidth)
            .attr('height', faultBandHeight)
            .attr('fill', occurrence.alarmLevel == '0' ? '#dc2626' : occurrence.alarmLevel == '1' ? '#facc15' : '#3b82f6') //根据等级确定颜色：严重红色，一般黄色，提醒蓝色
            .attr('opacity', 0.8)
            .append('title')
            .text(`${fault.name}: 故障期间 (${startTime} - ${endTime})`)
        }
      })

      // 添加故障名称标签（在色带左侧）
      bandGroup.append('text')
        .attr('x', margin.left + 5)
        .attr('y', bandY + faultBandHeight / 2)
        .attr('text-anchor', 'start')
        .attr('dominant-baseline', 'middle')
        .attr('font-size', '10px')
        .attr('font-weight', 'bold')
        .attr('fill', '#fff')
        .text(fault.name)
        .style('pointer-events', 'none')
    })
  }
}

const faultDisplayMode = ref('line')

const updateFaultDisplayMode = (mode: string, faultIds: string[], faultList: any[]) => {
  // 清除现有的故障显示元素
  svg.selectAll('.fault-bubble').remove()
  svg.selectAll('.fault-line').remove()
  svg.selectAll('.fault-band').remove()

  if (mode) {
    faultDisplayMode.value = mode
  }

  if (faultIds && faultList) {
    currentSelectedFaultIds.value = faultIds
    currentFaultList.value = faultList
  }
  if(selectedParametersList.value.length == 0) {
    faultBandsAreaHeight.value = 0
    return
  }
  updateFaultBandsHeight(currentSelectedFaultIds.value)
  if (faultDisplayMode.value === 'line') {
    // 只显示红色竖线
    faultBandsAreaHeight.value = 0
    drawFaultLines(currentSelectedFaultIds.value, currentFaultList.value)
  } else if (faultDisplayMode.value === 'band') {
    // 只显示色带
    drawFaultBands(currentSelectedFaultIds.value, currentFaultList.value)
  } else if (faultDisplayMode.value === 'both') {
    // 两者都显示
    drawFaultBands(currentSelectedFaultIds.value, currentFaultList.value)
    drawFaultLines(currentSelectedFaultIds.value, currentFaultList.value)
  }
  
  // 更新坐标轴
  updateAxes()
      
  // 更新缩放窗口位置，但不重置到初始状态
  updateZoomWindowPosition()
  const currentIsRectZoomActive = isRectZoomActive.value
  const currentRectZoomYRanges = JSON.parse(JSON.stringify(rectZoomYRanges.value || {}))
  const currentZoomMode = zoomType.value // 添加当前缩放模式
  updateFaultArea.value = true
  // // 如果处于矩形缩放或垂直缩放状态，恢复之前保存的Y轴范围
  if (needUpdateFaultArea.value) {
    if ((currentIsRectZoomActive || currentZoomMode === 'vertical') && Object.keys(currentRectZoomYRanges).length > 0) {
      // 恢复矩形缩放状态
      isRectZoomActive.value = true
      
      // 将之前保存的Y轴范围应用到新的参数上
      // 对于新增的参数，如果没有Y轴范围，使用全局Y轴范围或计算新范围
      rectZoomYRanges.value = currentRectZoomYRanges
      
      // 使用保存的Y轴范围更新图表
      updateChartWithParamYScales()
    } else {
      // 不在矩形缩放测量模式下，使用普通更新
      updateChart(false)
      // saveViewSettings()
    }
  } else {
    needUpdateFaultArea.value = true
  }
}
// 初始化图像
const resetSvg = () => {
  selectedParametersList.value = []
  selectedParametersListInView.value = []
  emit('clearSelectedVirtualParameter')
  // 重置x轴范围
  initialDomain.value = [Infinity, -Infinity]
  xScale.domain([Infinity, -Infinity])
  // 清空图像
  svg.selectAll('*').remove()
  // 重新绘制图像
  initChart()
}

// 重新查询数据绘图
const refreshDataViewer = () => {
  // 重置x轴范围
  initialDomain.value = [Infinity, -Infinity]
  xScale.domain([Infinity, -Infinity])

  // 重新绘制图像
  initChart()
  const startTime = localStorage.getItem('startTime')
  const endTime = localStorage.getItem('endTime')
  const params = prepareApiParams(startTime, endTime)
  queryChartData(params).then((data) => {
    if (!data || data.length === 0) saveViewSettings();
    // 如果当前没有有效的X轴范围，则计算一个新的
  
    // RJ模式：处理时间字符串
    let minTime: Date | null = null;
    let maxTime: Date | null = null;

    defaultData.value.forEach((channel: any) => {
      // 遍历该通道的所有参数
      Object.entries(channel.parameters).forEach(
        ([paramId, paramData]: [string, any]) => {
          if (!paramData || !Array.isArray(paramData)) return;

          if (paramData.length > 0) {
            paramData.forEach((d: any) => {
              // 将时间字符串转换为Date对象
              const timestamp = new Date(d.timestamp);

              if (!isNaN(timestamp.getTime())) {
                // 检查Date是否有效
                if (!minTime || timestamp < minTime) {
                  minTime = timestamp;
                }
                if (!maxTime || timestamp > maxTime) {
                  maxTime = timestamp;
                }
              }
            });
          }
        }
      );
    });

    if (minTime && maxTime) {
      // 将时间精确到秒级，去除毫秒
      const roundedMinTime = new Date(Math.floor(minTime.getTime() / 1000) * 1000);
      const roundedMaxTime = new Date( Math.floor(maxTime.getTime() / 1000) * 1000);
      initialDomain.value = [roundedMinTime, roundedMaxTime];
      xScale.domain([roundedMinTime, roundedMaxTime]);
    }
   
    // 重置缩放相关
    // 重置矩形缩放标志
    isRectZoomActive.value = false;
    skipUserYAxisSettings.value = false;
    rectZoomYRanges.value = {};
    zoomHistory.value = []
    // 更新坐标轴
    updateAxes();
    // 更新缩放窗口位置，但不重置到初始状态
    updateZoomWindowPosition();
    if (isChangeTask.value) {
      saveViewSettings(true);
      isChangeTask.value = false;
    } else {
      saveViewSettings();
    }

    // 延迟处理，确保图表已更新
    setTimeout(() => {
      // 如果在测量模式下，恢复测量游标
      if (isMeasuring.value) {
        // 确保有保存的游标位置
        if (!cursor1Position.value) cursor1Position.value = initialDomain.value[0];
        if (!cursor2Position.value) cursor2Position.value = initialDomain.value[1];
        
        console.log('从无参数到有参数，恢复测量游标', cursor1Position.value, cursor2Position.value)
        updateMeasurementCursors();
        if (selectedParametersList.value && selectedParametersList.value.length > 0) {
          updateMeasurementResults();
        }
      } else if (currentCursorPosition.value != undefined) {
        // 如果在普通游标模式下，恢复普通游标
        console.log("恢复游标线:", currentCursorPosition.value);
        const x = xScale(currentCursorPosition.value);
        updateCursorPosition(x, currentCursorPosition.value);
      }
    }, 300);
  });

  // 同时请求概览图数据（完整范围）
  if (zoomHistory.value.length > 0) {
    queryOverviewData(params).then((data) => {
      // 数据加载完成后显示缩放带概览图
      nextTick(() => {
        if (svg) {
          const zoomBarGroup = svg.select(".zoom-bar-group");
          if (!zoomBarGroup.empty() && chartContainer.value) {
            try {
              const width = chartContainer.value.clientWidth;
              const height = chartContainer.value.clientHeight;
              // 在缩放带上显示整体数据概览
              updateOverviewChart(zoomBarGroup as d3.Selection<SVGGElement, unknown, null, undefined>, width, height)
            } catch (error) {
              console.error('更新缩放带概览图出错:', error)
            }
          }
        }
      });
    });
  }
}

// 需要在组件末尾通过defineExpose暴露此方法
defineExpose({
  showParameterYAxis,
  resetSvg,
  handleHighlightLine,
  handleHighlightAllChannels,
  drawFaultLines,
  changeFaultMode,
  updateFaultDisplayMode,
  switchToMeasureMode,
  updateCursors,
  updateAnnotate,
  clearAnnotationHighlights,
  clearAllAnnotationHighlights,
  refreshDataViewer
})

// 添加resize事件防抖计时器
let resizeTimeout: number | null = null

// 确保在窗口调整大小时也更新滚动容器
window.addEventListener('resize', () => {
  // 防抖处理
  if (resizeTimeout) clearTimeout(resizeTimeout)
  resizeTimeout = setTimeout(() => {
    updateDiscreteArea()
  }, 200)
})
</script>

<style scoped>
/*@import './DataViewer.css';*/
@import '../../../src/assets/css/theme.css';
@import '../../../src/assets/css/buttons.css';
@import '../../../src/assets/css/modal.css';
*{
  -webkit-touch-callout:none; /*系统默认菜单被禁用*/
  -webkit-user-select:none; /*webkit浏览器*/
  -khtml-user-select:none; /*早期浏览器*/
  -moz-user-select:none;/*火狐*/
  -ms-user-select:none; /*IE10*/
  user-select:none;
}

/* 添加loading样式 */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--loading-bg-color);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.spinner {
  border: 5px solid #fcfcfc;
  border-top: 5px solid #3498db;
  border-radius: 50%;
  width: 40px;
  height: 40px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.data-viewer {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
  box-sizing: border-box;
  overflow: hidden;
  outline: none; /* 去除默认的焦点边框样式 */
  border: none;
}

.btn {
  padding: 4px 8px;
  background: #fff;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
  font-size: 14px;
  line-height: 1.5;
  cursor: pointer;
}

.btn:hover {
  color: #40a9ff;
  border-color: #40a9ff;
}

.btn-primary {
  color: #fff;
  background: #1890ff;
  border-color: #1890ff;
}

.btn-primary:hover {
  color: #fff;
  background: #40a9ff;
  border-color: #40a9ff;
}

.chart-container {
  flex: 1;
  min-height: 0;
  position: relative;
  overflow: hidden;
  width: 100%;
  background: var(--sub-plate-bg-color);
}

.controls {
  padding: 16px 16px 8px 16px;
  //border-top: 1px solid #f0f0f0;
  background-color: var(--sub-plate-bg-color);
  width: 100%;
  border-bottom: 1px solid var(--border-color-base);
  gap: 10px;
}

.controls .flex {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  row-gap: 6px;
}

/* 工具下拉菜单样式 */
.tools-dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-menu {
  position: absolute;
  top: 100%;
  left: 0;
    background: var(--select-dropdown-bg);
  border: 1px solid var(--select-border-color);
  border-radius: 6px;
  box-sizing: border-box;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  min-width: 120px;
  z-index: 1000;
  margin-top: 4px;
  overflow: hidden;
}

.dropdown-item {
  background: var(--select-dropdown-bg);
  padding: 8px 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text-color);
  transition: all 0.2s ease;
  border-bottom: 1px solid var(--select-border-color);
}

.dropdown-item:last-child {
  border-bottom: none;
}

.dropdown-item:hover {
  background: var(--text-color);
  color: var(--select-dropdown-bg);
}

.dropdown-item-active {
    background: var(--text-color);
    color: var(--select-dropdown-bg);
  font-weight: 600;
}

.dropdown-item svg {
  width: 16px;
  height: 16px;
  color: inherit;
}

@media (max-width: 1024px) {
  /* 工具下拉菜单在小屏幕下的适配 */
  .dropdown-menu {
    min-width: 120px;
    margin-top: 2px;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  }

  .dropdown-item {
    padding: 4px 8px;
    gap: 6px;
    font-size: 12px;
  }

  .dropdown-item svg {
    width: 14px;
    height: 14px;
  }
}

/* 添加键盘操作提示 */
.keyboard-hint {
  margin-top: 10px;
  font-size: 13px;
  color: #888;
  text-align: center;
  width: 100%;
}

.polygon-data {
  padding: 24px;
  flex: 1;
  overflow: auto;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.polygon-info {
  margin-bottom: 16px;
}

.polygon-actions {
  margin-top: 16px;
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* 添加回原先的一些重要样式 */
.line {
  fill: none;
  stroke-width: 1.5;
}

.grid line {
  stroke: #f0f0f0;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  stroke-width: 0;
}

.cursor-line {
  pointer-events: none;
}

.cursor-point {
  pointer-events: none;
}

.axis {
  font-size: 12px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.zoom-selection {
  pointer-events: none;
}

.cursor-line-1,
.cursor-line-2 {
  pointer-events: all;
}

.polygon-selection {
  pointer-events: fill; /* 从none改为fill，允许点击多边形区域 */
}

.polygon-point {
  cursor: pointer;
  pointer-events: all; /* 确保点可点击 */
}

.temp-line {
  pointer-events: none;
}

.discrete-group {
  pointer-events: none;
}

.discrete-label {
  pointer-events: none;
  font-size: 10px;
}

.discrete-band {
  pointer-events: none;
}

.discrete-band:hover {
  opacity: 1;
}
.discrete-band-hover {
  opacity: 0.85 !important;
  filter: brightness(1.2);
}

.zoom-window {
  pointer-events: none;
}

.zoom-handle {
  pointer-events: none;
}

.param-section {
  margin-bottom: 10px;
  overflow: hidden;
}

input[type="color"] {
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  cursor: pointer;
}

input[type="number"] {
  height: 26px;
  width: 50px;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
  padding: 0 5px;
  font-size: 0.875rem;
}

select {
  height: 26px;
  width: 80px;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
  padding: 0 5px;
  font-size: 0.875rem;
}

/* 模态框样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1100;
  overflow: hidden;
}

.modal-content {
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  width: 90%;
  max-width: 1100px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  animation: modal-fade-in 0.3s ease;
}

@keyframes modal-fade-in {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.modal-header {
  padding: 16px 24px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.modal-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: bold;
}

.close-btn {
  background: transparent;
  border: none;
    color: var(--text-color);
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  /* transition: color 0.3s; */
}

.close-btn:hover {
  color: rgba(0, 0, 0, 0.85);
}

.modal-body {
  padding: 16px 24px;
  overflow-y: auto;
  max-height: calc(90vh - 130px); /* 减去header和footer的高度 */
}

.view-btn-cancel, .view-btn-primary {
  height: 36px;
  font-size: 0.875rem;
  padding: 0.35rem 0.75rem;
  border-radius: 0.375rem;
  cursor: pointer;
  transition: all 0.2s ease;
  background: rgba(255, 255, 255, 0.8);
  border: 1px solid rgba(100, 116, 139, 0.3);
  color: rgba(100, 116, 139, 0.9);
}

.view-btn-cancel:hover {
  border-color: rgba(107, 114, 128, 0.5);
  background-color: rgba(107, 114, 128, 0.05);
  color: rgba(75, 85, 99, 1);
  transform: translateY(-1px);
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

.view-btn-cancel:active {
  background-color: rgba(107, 114, 128, 0.1);
  border-color: rgba(107, 114, 128, 0.6);
  color: rgba(55, 65, 81, 1);
  transform: translateY(0);
  box-shadow: none;
}

.view-btn-primary {
  background: rgba(59, 130, 246, 0.05);
  border: 1px solid rgba(59, 130, 246, 0.2);
  color: #2563eb;
}

.view-btn-primary:hover {
  background: rgba(59, 130, 246, 0.1);
  border-color: rgba(59, 130, 246, 0.3);
  color: #2563eb;
  transform: translateY(-1px);
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}

.view-btn-primary:disabled {
  background: rgba(100, 116, 139, 0.1);
  border-color: rgba(100, 116, 139, 0.2);
  color: rgba(100, 116, 139, 0.5);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.settings-list {
  display: flex;
  flex-direction: column;
}

.no-data-message {
  color: #888;
  font-size: 14px;
  margin-top: 4px;
  text-align: center;
}

/* 添加参数Y轴样式 */
.param-y-axis .domain {
  stroke: #999;
  stroke-width: 1px;
}

.param-y-axis text {
  fill: #555;
}

.param-y-axis .axis-title {
  font-weight: bold;
  fill: #333;
}

.y-axis-background {
  stroke: #eee;
  stroke-width: 1px;
}

.param-setting-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 0;
  /* border-bottom: 1px solid #f0f0f0; */
  /* margin-bottom: 8px; */
}

.setting-param-name {
  font-size: 14px;
  width: 140px;
  word-break: break-all;
}

.control-group {
  display: flex;
  gap: 4px;
  align-items: center;
  justify-content: center;
}

.control-group label {
  font-size: 14px;
  color: var(--preview-text-color);
}

.control-group input {
  width: 80px;
}

.control-group select {
  width: 80px;
}

.tech-checkbox {
    display: flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    position: relative;
    flex: 1;
}

.tech-checkbox input {
    position: absolute;
    opacity: 0;
    cursor: pointer;
    height: 0;
    width: 0;
}

.checkbox-custom {
    position: relative;
    height: 18px;
    width: 18px;
    min-width: 18px;
    border-radius: 4px;
    border: 1px solid var(--btn-click-color);
    background-color: var(--ant-checkbox-inner);
    color: var(--global-color-white);
}

.tech-checkbox input:checked ~ .checkbox-custom {
    color: var(--global-color-white);
    background-color: var(--ant-checkbox-checked);
    border-color: var(--btn-click-color);
}

.checkbox-custom:after {
    content: "";
    position: absolute;
    display: none;
    left: 6px;
    top: 2px;
    width: 5px;
    height: 10px;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.tech-checkbox input:checked ~ .checkbox-custom:after {
    display: block;
}
.search-select {
    background: var(--select-background);
    color: var(--text-color);
    width: 100%;
    border: 1px solid var(--input-border-color);
    border-radius: 4px;
}

.search-select:focus {
    outline: none;
    border-color: var(--btn-hover-color);
}


.y-axis-range {
  display: flex;
  align-items: center;
  gap: 8px;
}

.range-input {
  width: 60px;
  height: 26px;
    background: var(--select-background);
    color: var(--text-color);
    border: 1px solid var(--input-border-color);
  border-radius: 2px;
  padding: 0 5px;
  font-size: 0.875rem;
}

.reset-btn {
  background: transparent;
  border: none;
  font-size: 14px;
  color: #666;
  cursor: pointer;
  padding: 0 2px;
}

.reset-btn:hover {
  color: #40a9ff;
}

.tech-btn {
  font-size: 15px;
}

/* 1024*768分辨率适配 */
@media (max-width: 1024px) {
  .data-viewer {
    font-size: 12px;
  }

  .controls {
    padding: 8px;
    gap: 4px;
  }

  .controls .flex {
    gap: 4px;
    row-gap: 4px;
    width: 100%;
  }

  .tech-btn svg {
    width: 12px;
    height: 12px;
  }

  .tech-btn {
    padding: 4px 8px;
    height: 24px;
    font-size: 12px;
    white-space: nowrap;
  }

  .keyboard-hint {
    font-size: 10px;
  }

  .modal-content {
    width: 90%;
    max-width: 960px;
    .control-group label {
      font-size: 11px;
    }
  }

  .modal-header {
    padding: 10px 16px;
  }

  .modal-header h3 {
    font-size: 14px;
  }

  /* 视图设置弹窗内部适配 */
  .param-setting-row {
    gap: 6px;
    padding: 8px;
  }

  .control-group {
    flex-direction: row;
    align-items: center;
  }

  .y-axis-range {
    flex-direction: row;
    gap: 4px;
  }

  .range-input {
    width: 70px;
  }

  /* 图表区域内部适配 */
  .chart-container {
    height: calc(100% - 50px);
  }

  .setting-param-name {
    font-size: 12px;
    width: 100px;
    word-break: break-all;
  }
}

/* 游标标签样式 */
.cursor-label-group {
  pointer-events: all;
}

.cursor-label-bg {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
  transition: all 0.2s ease;
}

.cursor-label-bg:hover {
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
  stroke-width: 1.5;
}

.cursor-label-text {
  pointer-events: none;
  text-anchor: start;
  dominant-baseline: central;
}

</style>